-
Notifications
You must be signed in to change notification settings - Fork 26
Bump cmf-sdk-go v0.0.5 to v0.0.6 and fix breaking changes #3320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1c59e6e
adf7963
d18db04
2704dd1
d16c1b8
a182d1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,6 +60,21 @@ func (c *command) validComputePoolArgs(cmd *cobra.Command, args []string) []stri | |
| return c.autocompleteComputePools(cmd, args) | ||
| } | ||
|
|
||
| // extractComputePoolPhase extracts the phase string from the generic status map. | ||
| // ComputePool.Status is typed as *map[string]map[string]interface{} in SDK v0.0.6. | ||
| func extractComputePoolPhase(pool cmfsdk.ComputePool) string { | ||
| if pool.Status == nil { | ||
| return "" | ||
| } | ||
| status := pool.GetStatus() | ||
| if phaseMap, ok := status["phase"]; ok { | ||
| if value, ok := phaseMap["value"].(string); ok { | ||
| return value | ||
| } | ||
| } | ||
| return "" | ||
| } | ||
|
|
||
| func convertSdkComputePoolToLocalComputePool(sdkComputePool cmfsdk.ComputePool) LocalComputePool { | ||
| localPool := LocalComputePool{ | ||
| ApiVersion: sdkComputePool.ApiVersion, | ||
|
|
@@ -77,9 +92,9 @@ func convertSdkComputePoolToLocalComputePool(sdkComputePool cmfsdk.ComputePool) | |
| }, | ||
| } | ||
|
|
||
| if sdkComputePool.Status != nil { | ||
| if phase := extractComputePoolPhase(sdkComputePool); phase != "" { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we always call this method, also when the CLI runs against older CMF versions? Probably yes? because the response has not changed, only the SDK. |
||
| localPool.Status = &LocalComputePoolStatus{ | ||
| Phase: sdkComputePool.Status.Phase, | ||
| Phase: phase, | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| package flink | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "encoding/json" | ||
| "errors" | ||
| "fmt" | ||
| "io" | ||
|
|
@@ -231,7 +233,10 @@ func (cmfClient *CmfRestClient) UpdateApplication(ctx context.Context, environme | |
| // CreateEnvironment Create an environment. | ||
| // Internally, since the call for Create and Update is the same, we check if the environment exists before creation. | ||
| func (cmfClient *CmfRestClient) CreateEnvironment(ctx context.Context, postEnvironment cmfsdk.PostEnvironment) (cmfsdk.Environment, error) { | ||
| environmentName := postEnvironment.Name | ||
| environmentName := postEnvironment.GetName() | ||
| if environmentName == "" { | ||
| return cmfsdk.Environment{}, fmt.Errorf("environment name is required") | ||
| } | ||
| _, httpResponse, _ := cmfClient.EnvironmentsApi.GetEnvironment(ctx, environmentName).Execute() | ||
| // check if the environment exists by checking the status code | ||
| if httpResponse != nil && httpResponse.StatusCode == http.StatusOK { | ||
|
|
@@ -281,10 +286,13 @@ func (cmfClient *CmfRestClient) ListEnvironments(ctx context.Context) ([]cmfsdk. | |
| return environments, nil | ||
| } | ||
|
|
||
| // UpdateEnvironment Create an environment. | ||
| // Internally, since the call for Create and Update is the same, we check if the environment exists before updation. | ||
| // UpdateEnvironment updates an environment. | ||
| // Internally, since the call for Create and Update is the same, we check if the environment exists before updating. | ||
| func (cmfClient *CmfRestClient) UpdateEnvironment(ctx context.Context, postEnvironment cmfsdk.PostEnvironment) (cmfsdk.Environment, error) { | ||
| environmentName := postEnvironment.Name | ||
| environmentName := postEnvironment.GetName() | ||
| if environmentName == "" { | ||
| return cmfsdk.Environment{}, fmt.Errorf("environment name is required") | ||
| } | ||
| _, httpResponse, err := cmfClient.EnvironmentsApi.GetEnvironment(ctx, environmentName).Execute() | ||
| // check if the environment exists by checking the status code | ||
| if httpResponse != nil && httpResponse.StatusCode == http.StatusNotFound { | ||
|
|
@@ -414,6 +422,9 @@ func (cmfClient *CmfRestClient) CreateComputePool(ctx context.Context, environme | |
| return cmfsdk.ComputePool{}, fmt.Errorf("compute pool name is required") | ||
| } | ||
| outputComputePool, httpResponse, err := cmfClient.SQLApi.CreateComputePool(ctx, environment).ComputePool(computePool).Execute() | ||
| if pool, ok := tryComputePoolFallback(httpResponse, err, unmarshalComputePool); ok { | ||
| return pool, nil | ||
| } | ||
| if parsedErr := parseSdkError(httpResponse, err); parsedErr != nil { | ||
| return cmfsdk.ComputePool{}, fmt.Errorf(`failed to create compute pool "%s" in the environment "%s": %s`, computePoolName, environment, parsedErr) | ||
| } | ||
|
|
@@ -427,6 +438,9 @@ func (cmfClient *CmfRestClient) DeleteComputePool(ctx context.Context, environme | |
|
|
||
| func (cmfClient *CmfRestClient) DescribeComputePool(ctx context.Context, environment, computePool string) (cmfsdk.ComputePool, error) { | ||
| cmfComputePool, httpResponse, err := cmfClient.SQLApi.GetComputePool(ctx, environment, computePool).Execute() | ||
| if pool, ok := tryComputePoolFallback(httpResponse, err, unmarshalComputePool); ok { | ||
| return pool, nil | ||
| } | ||
| if parsedErr := parseSdkError(httpResponse, err); parsedErr != nil { | ||
| return cmfsdk.ComputePool{}, fmt.Errorf(`failed to describe compute pool "%s" in the environment "%s": %s`, computePool, environment, parsedErr) | ||
| } | ||
|
|
@@ -442,6 +456,11 @@ func (cmfClient *CmfRestClient) ListComputePools(ctx context.Context, environmen | |
|
|
||
| for !done { | ||
| computePoolsPage, httpResponse, err := cmfClient.SQLApi.GetComputePools(ctx, environment).Page(currentPageNumber).Size(pageSize).Execute() | ||
| if parsed, ok := tryComputePoolFallback(httpResponse, err, unmarshalComputePoolsPage); ok { | ||
| computePools = append(computePools, parsed...) | ||
| currentPageNumber, done = extractPageOptions(len(parsed), currentPageNumber) | ||
| continue | ||
| } | ||
| if parsedErr := parseSdkError(httpResponse, err); parsedErr != nil { | ||
| return nil, fmt.Errorf(`failed to list compute pools in the environment "%s": %s`, environment, parsedErr) | ||
| } | ||
|
|
@@ -570,6 +589,81 @@ func (cmfClient *CmfRestClient) DeleteCatalog(ctx context.Context, catalogName s | |
| return parseSdkError(httpResp, err) | ||
| } | ||
|
|
||
| // --- ComputePool deserialization workaround (SDK v0.0.6) --- | ||
| // | ||
| // The SDK types ComputePool.Status as *map[string]map[string]interface{} but the | ||
| // API returns a flat object like {"phase":"RUNNING","message":null}.These helpers | ||
| // re-parse the buffered response body with correct handling. | ||
| func unmarshalComputePool(data []byte) (cmfsdk.ComputePool, error) { | ||
| var wrapper struct { | ||
| cmfsdk.ComputePool | ||
| Status json.RawMessage `json:"status"` | ||
| } | ||
| if err := json.Unmarshal(data, &wrapper); err != nil { | ||
| return cmfsdk.ComputePool{}, err | ||
| } | ||
| pool := wrapper.ComputePool | ||
| var status struct { | ||
| Phase string `json:"phase"` | ||
| } | ||
| if json.Unmarshal(wrapper.Status, &status) == nil && status.Phase != "" { | ||
| s := map[string]map[string]interface{}{ | ||
| "phase": {"value": status.Phase}, | ||
| } | ||
| pool.Status = &s | ||
| } | ||
| return pool, nil | ||
| } | ||
|
|
||
| func unmarshalComputePoolsPage(data []byte) ([]cmfsdk.ComputePool, error) { | ||
| var page struct { | ||
| Items []json.RawMessage `json:"items"` | ||
| } | ||
| if err := json.Unmarshal(data, &page); err != nil { | ||
| return nil, err | ||
| } | ||
| pools := make([]cmfsdk.ComputePool, 0, len(page.Items)) | ||
| for _, raw := range page.Items { | ||
| pool, err := unmarshalComputePool(raw) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| pools = append(pools, pool) | ||
| } | ||
| return pools, nil | ||
| } | ||
|
|
||
| // readFallbackBody returns the HTTP response body when the SDK's Execute() returned | ||
| // an error despite a successful 2xx status — the ComputePool deserialization bug. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is "the ComputePool deserialization bug"? I don't think this is a bug? |
||
| // Returns nil if the condition doesn't apply or the body can't be read. | ||
| func readFallbackBody(httpResponse *http.Response, err error) []byte { | ||
| if err == nil || httpResponse == nil || httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { | ||
| return nil | ||
| } | ||
| body, readErr := io.ReadAll(httpResponse.Body) | ||
| if readErr != nil { | ||
| return nil | ||
| } | ||
| return body | ||
| } | ||
|
|
||
| // tryComputePoolFallback runs the fallback parse for a ComputePool-bearing response. | ||
| // Returns (result, true) on success. On failure, re-buffers the response body so | ||
| // parseSdkError can still read it, and returns (zero, false). | ||
| func tryComputePoolFallback[T any](httpResponse *http.Response, err error, unmarshal func([]byte) (T, error)) (T, bool) { | ||
| var zero T | ||
| body := readFallbackBody(httpResponse, err) | ||
| if body == nil { | ||
| return zero, false | ||
| } | ||
| result, parseErr := unmarshal(body) | ||
| if parseErr != nil { | ||
| httpResponse.Body = io.NopCloser(bytes.NewBuffer(body)) | ||
| return zero, false | ||
| } | ||
| return result, true | ||
| } | ||
|
|
||
| // Returns the next page number and whether we need to fetch more pages or not. | ||
| func extractPageOptions(receivedItemsLength int, currentPageNumber int32) (int32, bool) { | ||
| if receivedItemsLength == 0 { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it make sense to move this utility into the SDK?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that we don't do human code push in cmf-go-sdk, but if we want to : Adds a custom UnmarshalJSON method on the ComputePool type (in a new file v1/model_compute_pool_custom.go ) seems to be the better fix, which completely removes "custom unmarshaling with json.RawMessage embedding" from cli repo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only worry about human pushes in the sdk repo is that the code generator is overwriting human written code / utils.
But when we started the go sdk repo, I actually had additional, non generated code for shared utilities in mind.