Compare commits

...

3 commits
v2.2.0 ... main

Author SHA1 Message Date
51c24718e4
Stop considering empty array responses to be errors
Some checks failed
test / test (push) Failing after 1s
ci / goreleaser (push) Failing after 1s
2026-02-10 15:51:39 +01:00
d38707aea4
Add support for v1 reading of array responses
Some checks failed
test / test (push) Failing after 1s
ci / goreleaser (push) Failing after 0s
2026-02-10 15:31:08 +01:00
ff6441dafc
removed not specified field
All checks were successful
test / test (push) Successful in 56s
2026-01-19 14:54:42 +01:00
10 changed files with 48 additions and 23 deletions

BIN
comprehensive Executable file

Binary file not shown.

View file

@ -360,7 +360,6 @@ func (p *EdgeConnectPlanner) getCurrentInstanceState(ctx context.Context, desire
CloudletName: instance.Key.CloudletKey.Name, CloudletName: instance.Key.CloudletKey.Name,
FlavorName: instance.Flavor.Name, FlavorName: instance.Flavor.Name,
State: instance.State, State: instance.State,
PowerState: instance.PowerState,
Exists: true, Exists: true,
LastUpdated: time.Now(), // EdgeConnect doesn't provide this LastUpdated: time.Now(), // EdgeConnect doesn't provide this
} }

View file

@ -223,8 +223,7 @@ func TestPlanExistingDeploymentNoChanges(t *testing.T) {
Flavor: v2.Flavor{ Flavor: v2.Flavor{
Name: "small", Name: "small",
}, },
State: "Ready", State: "Ready",
PowerState: "PowerOn",
} }
mockClient.On("ShowApp", mock.Anything, mock.AnythingOfType("v2.AppKey"), "US"). mockClient.On("ShowApp", mock.Anything, mock.AnythingOfType("v2.AppKey"), "US").

View file

@ -170,9 +170,6 @@ type InstanceState struct {
// State of the instance (e.g., "Ready", "Pending", "Error") // State of the instance (e.g., "Ready", "Pending", "Error")
State string State string
// PowerState of the instance
PowerState string
// LastUpdated timestamp when the instance was last modified // LastUpdated timestamp when the instance was last modified
LastUpdated time.Time LastUpdated time.Time

View file

@ -213,10 +213,20 @@ func (c *Client) parseStreamingAppInstanceResponse(resp *http.Response, result i
var errorMessage string var errorMessage string
parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error { parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error {
// On permission denied, Edge API returns just an empty array []! if len(line) == 0 {
if len(line) == 0 || line[0] == '[' {
return fmt.Errorf("%w", ErrFaultyResponsePerhaps403) return fmt.Errorf("%w", ErrFaultyResponsePerhaps403)
} }
// Handle array responses (current API format)
if line[0] == '[' {
var directInstances []AppInstance
if err := json.Unmarshal(line, &directInstances); err != nil {
return err
}
appInstances = append(appInstances, directInstances...)
return nil
}
// Try parsing as ResultResponse first (error format) // Try parsing as ResultResponse first (error format)
var resultResp ResultResponse var resultResp ResultResponse
if err := json.Unmarshal(line, &resultResp); err == nil && resultResp.Result.Message != "" { if err := json.Unmarshal(line, &resultResp); err == nil && resultResp.Result.Message != "" {
@ -228,7 +238,7 @@ func (c *Client) parseStreamingAppInstanceResponse(resp *http.Response, result i
return nil return nil
} }
// Try parsing as Response[AppInstance] // Try parsing as Response[AppInstance] (legacy streaming format)
var response Response[AppInstance] var response Response[AppInstance]
if err := json.Unmarshal(line, &response); err != nil { if err := json.Unmarshal(line, &response); err != nil {
return err return err

View file

@ -180,10 +180,23 @@ func (c *Client) parseStreamingResponse(resp *http.Response, result interface{})
var responses []Response[App] var responses []Response[App]
parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error { parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error {
// On permission denied, Edge API returns just an empty array []! if len(line) == 0 {
if len(line) == 0 || line[0] == '[' {
return fmt.Errorf("%w", ErrFaultyResponsePerhaps403) return fmt.Errorf("%w", ErrFaultyResponsePerhaps403)
} }
// Handle array responses (current API format)
if line[0] == '[' {
var directApps []App
if err := json.Unmarshal(line, &directApps); err != nil {
return err
}
for _, app := range directApps {
responses = append(responses, Response[App]{Data: app})
}
return nil
}
// Try parsing as Response[App] (legacy streaming format)
var response Response[App] var response Response[App]
if err := json.Unmarshal(line, &response); err != nil { if err := json.Unmarshal(line, &response); err != nil {
return err return err

View file

@ -229,6 +229,23 @@ func (c *Client) parseStreamingCloudletResponse(resp *http.Response, result inte
var responses []Response[Cloudlet] var responses []Response[Cloudlet]
parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error { parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error {
if len(line) == 0 {
return fmt.Errorf("%w", ErrFaultyResponsePerhaps403)
}
// Handle array responses (current API format)
if line[0] == '[' {
var directCloudlets []Cloudlet
if err := json.Unmarshal(line, &directCloudlets); err != nil {
return err
}
for _, cl := range directCloudlets {
responses = append(responses, Response[Cloudlet]{Data: cl})
}
return nil
}
// Try parsing as Response[Cloudlet] (legacy streaming format)
var response Response[Cloudlet] var response Response[Cloudlet]
if err := json.Unmarshal(line, &response); err != nil { if err := json.Unmarshal(line, &response); err != nil {
return err return err

View file

@ -300,8 +300,7 @@ func TestUpdateAppInstance(t *testing.T) {
Name: "testapp", Name: "testapp",
Version: "1.0.0", Version: "1.0.0",
}, },
Flavor: Flavor{Name: "m4.medium"}, Flavor: Flavor{Name: "m4.medium"},
PowerState: "PowerOn",
}, },
}, },
mockStatusCode: 200, mockStatusCode: 200,

View file

@ -113,7 +113,6 @@ const (
AppInstFieldConfigsKind = "27.1" AppInstFieldConfigsKind = "27.1"
AppInstFieldConfigsConfig = "27.2" AppInstFieldConfigsConfig = "27.2"
AppInstFieldHealthCheck = "29" AppInstFieldHealthCheck = "29"
AppInstFieldPowerState = "31"
AppInstFieldExternalVolumeSize = "32" AppInstFieldExternalVolumeSize = "32"
AppInstFieldAvailabilityZone = "33" AppInstFieldAvailabilityZone = "33"
AppInstFieldVmFlavor = "34" AppInstFieldVmFlavor = "34"
@ -201,7 +200,6 @@ type App struct {
GlobalID string `json:"global_id,omitempty"` GlobalID string `json:"global_id,omitempty"`
CreatedAt string `json:"created_at,omitempty"` CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"` UpdatedAt string `json:"updated_at,omitempty"`
Fields []string `json:"fields,omitempty"`
} }
// AppInstance represents a deployed application instance // AppInstance represents a deployed application instance
@ -216,8 +214,6 @@ type AppInstance struct {
UniqueID string `json:"unique_id,omitempty"` UniqueID string `json:"unique_id,omitempty"`
CreatedAt string `json:"created_at,omitempty"` CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"` UpdatedAt string `json:"updated_at,omitempty"`
PowerState string `json:"power_state,omitempty"`
Fields []string `json:"fields,omitempty"`
} }
// Cloudlet represents edge infrastructure // Cloudlet represents edge infrastructure

View file

@ -203,7 +203,6 @@ func runComprehensiveWorkflow(ctx context.Context, c *v2.Client, config Workflow
fmt.Printf(" • Cloudlet: %s/%s\n", instanceDetails.Key.CloudletKey.Organization, instanceDetails.Key.CloudletKey.Name) fmt.Printf(" • Cloudlet: %s/%s\n", instanceDetails.Key.CloudletKey.Organization, instanceDetails.Key.CloudletKey.Name)
fmt.Printf(" • Flavor: %s\n", instanceDetails.Flavor.Name) fmt.Printf(" • Flavor: %s\n", instanceDetails.Flavor.Name)
fmt.Printf(" • State: %s\n", instanceDetails.State) fmt.Printf(" • State: %s\n", instanceDetails.State)
fmt.Printf(" • Power State: %s\n", instanceDetails.PowerState)
// 6. List Application Instances // 6. List Application Instances
fmt.Println("\n6⃣ Listing application instances...") fmt.Println("\n6⃣ Listing application instances...")
@ -328,11 +327,7 @@ func waitForInstanceReady(ctx context.Context, c *v2.Client, instanceKey v2.AppI
continue continue
} }
fmt.Printf(" 📊 Instance state: %s", instance.State) fmt.Printf(" 📊 Instance state: %s\n", instance.State)
if instance.PowerState != "" {
fmt.Printf(" (power: %s)", instance.PowerState)
}
fmt.Printf("\n")
// Check if instance is ready (not in creating state) // Check if instance is ready (not in creating state)
state := strings.ToLower(instance.State) state := strings.ToLower(instance.State)