diff --git a/cmd/app.go b/cmd/app.go index 02125fc..37218bf 100644 --- a/cmd/app.go +++ b/cmd/app.go @@ -37,7 +37,7 @@ func validateBaseURL(baseURL string) error { return fmt.Errorf("user and or password should not be set") } - if !(url.Path == "" || url.Path == "/") { + if url.Path != "" && url.Path != "/" { return fmt.Errorf("should not contain any path '%s'", url.Path) } @@ -291,12 +291,18 @@ func init() { cmd.Flags().StringVarP(&appName, "name", "n", "", "application name") cmd.Flags().StringVarP(&appVersion, "version", "v", "", "application version") cmd.Flags().StringVarP(®ion, "region", "r", "", "region (required)") - cmd.MarkFlagRequired("org") - cmd.MarkFlagRequired("region") + if err := cmd.MarkFlagRequired("org"); err != nil { + panic(err) + } + if err := cmd.MarkFlagRequired("region"); err != nil { + panic(err) + } } // Add required name flag for specific commands for _, cmd := range []*cobra.Command{createAppCmd, showAppCmd, deleteAppCmd} { - cmd.MarkFlagRequired("name") + if err := cmd.MarkFlagRequired("name"); err != nil { + panic(err) + } } } diff --git a/cmd/apply.go b/cmd/apply.go index e2affd0..cf2b37f 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -31,7 +31,7 @@ the necessary changes to deploy your applications across multiple cloudlets.`, Run: func(cmd *cobra.Command, args []string) { if configFile == "" { fmt.Fprintf(os.Stderr, "Error: configuration file is required\n") - cmd.Usage() + _ = cmd.Usage() os.Exit(1) } @@ -208,20 +208,6 @@ func runApplyV2(cfg *config.EdgeConnectConfig, manifestContent string, isDryRun return displayDeploymentResults(deployResult) } -type deploymentResult interface { - IsSuccess() bool - GetDuration() string - GetCompletedActions() []actionResult - GetFailedActions() []actionResult - GetError() error -} - -type actionResult interface { - GetType() string - GetTarget() string - GetError() error -} - func displayDeploymentResults(result interface{}) error { // Use reflection or type assertion to handle both v1 and v2 result types // For now, we'll use a simple approach that works with both @@ -288,7 +274,7 @@ func displayDeploymentResultsV2(deployResult *applyv2.ExecutionResult) error { func confirmDeployment() bool { fmt.Print("Do you want to proceed? (yes/no): ") var response string - fmt.Scanln(&response) + _, _ = fmt.Scanln(&response) switch response { case "yes", "y", "YES", "Y": @@ -305,5 +291,7 @@ func init() { applyCmd.Flags().BoolVar(&dryRun, "dry-run", false, "preview changes without applying them") applyCmd.Flags().BoolVar(&autoApprove, "auto-approve", false, "automatically approve the deployment plan") - applyCmd.MarkFlagRequired("file") + if err := applyCmd.MarkFlagRequired("file"); err != nil { + panic(err) + } } diff --git a/cmd/delete.go b/cmd/delete.go index 7124e61..dcc1614 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -31,7 +31,7 @@ Instances are always deleted before the application.`, Run: func(cmd *cobra.Command, args []string) { if deleteConfigFile == "" { fmt.Fprintf(os.Stderr, "Error: configuration file is required\n") - cmd.Usage() + _ = cmd.Usage() os.Exit(1) } @@ -273,7 +273,7 @@ func displayDeletionResultsV2(deleteResult *deletev2.DeletionResult) error { func confirmDeletion() bool { fmt.Print("Do you want to proceed with deletion? (yes/no): ") var response string - fmt.Scanln(&response) + _, _ = fmt.Scanln(&response) switch response { case "yes", "y", "YES", "Y": @@ -290,5 +290,7 @@ func init() { deleteCmd.Flags().BoolVar(&deleteDryRun, "dry-run", false, "preview deletion without actually deleting resources") deleteCmd.Flags().BoolVar(&deleteAutoApprove, "auto-approve", false, "automatically approve the deletion plan") - deleteCmd.MarkFlagRequired("file") + if err := deleteCmd.MarkFlagRequired("file"); err != nil { + panic(err) + } } diff --git a/cmd/instance.go b/cmd/instance.go index 0b78986..75868ce 100644 --- a/cmd/instance.go +++ b/cmd/instance.go @@ -230,17 +230,31 @@ func init() { cmd.Flags().StringVarP(&cloudletOrg, "cloudlet-org", "", "", "cloudlet organization (required)") cmd.Flags().StringVarP(®ion, "region", "r", "", "region (required)") - cmd.MarkFlagRequired("org") - cmd.MarkFlagRequired("name") - cmd.MarkFlagRequired("cloudlet") - cmd.MarkFlagRequired("cloudlet-org") - cmd.MarkFlagRequired("region") + if err := cmd.MarkFlagRequired("org"); err != nil { + panic(err) + } + if err := cmd.MarkFlagRequired("name"); err != nil { + panic(err) + } + if err := cmd.MarkFlagRequired("cloudlet"); err != nil { + panic(err) + } + if err := cmd.MarkFlagRequired("cloudlet-org"); err != nil { + panic(err) + } + if err := cmd.MarkFlagRequired("region"); err != nil { + panic(err) + } } // Add additional flags for create command createInstanceCmd.Flags().StringVarP(&appName, "app", "a", "", "application name (required)") createInstanceCmd.Flags().StringVarP(&appVersion, "version", "v", "", "application version") createInstanceCmd.Flags().StringVarP(&flavorName, "flavor", "f", "", "flavor name (required)") - createInstanceCmd.MarkFlagRequired("app") - createInstanceCmd.MarkFlagRequired("flavor") + if err := createInstanceCmd.MarkFlagRequired("app"); err != nil { + panic(err) + } + if err := createInstanceCmd.MarkFlagRequired("flavor"); err != nil { + panic(err) + } } diff --git a/cmd/root.go b/cmd/root.go index dd22f72..52ae3ca 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -44,19 +44,35 @@ func init() { rootCmd.PersistentFlags().StringVar(&apiVersion, "api-version", "v2", "API version to use (v1 or v2)") rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "enable debug logging") - viper.BindPFlag("base_url", rootCmd.PersistentFlags().Lookup("base-url")) - viper.BindPFlag("username", rootCmd.PersistentFlags().Lookup("username")) - viper.BindPFlag("password", rootCmd.PersistentFlags().Lookup("password")) - viper.BindPFlag("api_version", rootCmd.PersistentFlags().Lookup("api-version")) + if err := viper.BindPFlag("base_url", rootCmd.PersistentFlags().Lookup("base-url")); err != nil { + panic(err) + } + if err := viper.BindPFlag("username", rootCmd.PersistentFlags().Lookup("username")); err != nil { + panic(err) + } + if err := viper.BindPFlag("password", rootCmd.PersistentFlags().Lookup("password")); err != nil { + panic(err) + } + if err := viper.BindPFlag("api_version", rootCmd.PersistentFlags().Lookup("api-version")); err != nil { + panic(err) + } } func initConfig() { viper.AutomaticEnv() viper.SetEnvPrefix("EDGE_CONNECT") - viper.BindEnv("base_url", "EDGE_CONNECT_BASE_URL") - viper.BindEnv("username", "EDGE_CONNECT_USERNAME") - viper.BindEnv("password", "EDGE_CONNECT_PASSWORD") - viper.BindEnv("api_version", "EDGE_CONNECT_API_VERSION") + if err := viper.BindEnv("base_url", "EDGE_CONNECT_BASE_URL"); err != nil { + panic(err) + } + if err := viper.BindEnv("username", "EDGE_CONNECT_USERNAME"); err != nil { + panic(err) + } + if err := viper.BindEnv("password", "EDGE_CONNECT_PASSWORD"); err != nil { + panic(err) + } + if err := viper.BindEnv("api_version", "EDGE_CONNECT_API_VERSION"); err != nil { + panic(err) + } if cfgFile != "" { viper.SetConfigFile(cfgFile) diff --git a/internal/apply/v1/planner.go b/internal/apply/v1/planner.go index 001076c..bcfd043 100644 --- a/internal/apply/v1/planner.go +++ b/internal/apply/v1/planner.go @@ -323,12 +323,7 @@ func (p *EdgeConnectPlanner) getCurrentAppState(ctx context.Context, desired *Ap // Extract outbound connections from the app current.OutboundConnections = make([]SecurityRule, len(app.RequiredOutboundConnections)) for i, conn := range app.RequiredOutboundConnections { - current.OutboundConnections[i] = SecurityRule{ - Protocol: conn.Protocol, - PortRangeMin: conn.PortRangeMin, - PortRangeMax: conn.PortRangeMax, - RemoteCIDR: conn.RemoteCIDR, - } + current.OutboundConnections[i] = SecurityRule(conn) } return current, nil @@ -470,7 +465,9 @@ func (p *EdgeConnectPlanner) calculateManifestHash(manifestPath string) (string, if err != nil { return "", fmt.Errorf("failed to open manifest file: %w", err) } - defer file.Close() + defer func() { + _ = file.Close() + }() hasher := sha256.New() if _, err := io.Copy(hasher, file); err != nil { @@ -505,18 +502,20 @@ func (p *EdgeConnectPlanner) estimateDeploymentDuration(plan *DeploymentPlan) ti var duration time.Duration // App operations - if plan.AppAction.Type == ActionCreate { + switch plan.AppAction.Type { + case ActionCreate: duration += 30 * time.Second - } else if plan.AppAction.Type == ActionUpdate { + case ActionUpdate: duration += 15 * time.Second } // Instance operations (can be done in parallel) instanceDuration := time.Duration(0) for _, action := range plan.InstanceActions { - if action.Type == ActionCreate { + switch action.Type { + case ActionCreate: instanceDuration = max(instanceDuration, 2*time.Minute) - } else if action.Type == ActionUpdate { + case ActionUpdate: instanceDuration = max(instanceDuration, 1*time.Minute) } } diff --git a/internal/apply/v2/planner.go b/internal/apply/v2/planner.go index 52a5e18..61f15cd 100644 --- a/internal/apply/v2/planner.go +++ b/internal/apply/v2/planner.go @@ -323,12 +323,7 @@ func (p *EdgeConnectPlanner) getCurrentAppState(ctx context.Context, desired *Ap // Extract outbound connections from the app current.OutboundConnections = make([]SecurityRule, len(app.RequiredOutboundConnections)) for i, conn := range app.RequiredOutboundConnections { - current.OutboundConnections[i] = SecurityRule{ - Protocol: conn.Protocol, - PortRangeMin: conn.PortRangeMin, - PortRangeMax: conn.PortRangeMax, - RemoteCIDR: conn.RemoteCIDR, - } + current.OutboundConnections[i] = SecurityRule(conn) } return current, nil @@ -470,7 +465,9 @@ func (p *EdgeConnectPlanner) calculateManifestHash(manifestPath string) (string, if err != nil { return "", fmt.Errorf("failed to open manifest file: %w", err) } - defer file.Close() + defer func() { + _ = file.Close() + }() hasher := sha256.New() if _, err := io.Copy(hasher, file); err != nil { @@ -505,18 +502,20 @@ func (p *EdgeConnectPlanner) estimateDeploymentDuration(plan *DeploymentPlan) ti var duration time.Duration // App operations - if plan.AppAction.Type == ActionCreate { + switch plan.AppAction.Type { + case ActionCreate: duration += 30 * time.Second - } else if plan.AppAction.Type == ActionUpdate { + case ActionUpdate: duration += 15 * time.Second } // Instance operations (can be done in parallel) instanceDuration := time.Duration(0) for _, action := range plan.InstanceActions { - if action.Type == ActionCreate { + switch action.Type { + case ActionCreate: instanceDuration = max(instanceDuration, 2*time.Minute) - } else if action.Type == ActionUpdate { + case ActionUpdate: instanceDuration = max(instanceDuration, 1*time.Minute) } } diff --git a/sdk/edgeconnect/appinstance.go b/sdk/edgeconnect/appinstance.go index f655c98..4a1bda9 100644 --- a/sdk/edgeconnect/appinstance.go +++ b/sdk/edgeconnect/appinstance.go @@ -23,7 +23,9 @@ func (c *Client) CreateAppInstance(ctx context.Context, input *NewAppInstanceInp if err != nil { return fmt.Errorf("CreateAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "CreateAppInstance") @@ -56,7 +58,9 @@ func (c *Client) ShowAppInstance(ctx context.Context, appInstKey AppInstanceKey, if err != nil { return AppInstance{}, fmt.Errorf("ShowAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return AppInstance{}, fmt.Errorf("app instance %s/%s: %w", @@ -96,7 +100,9 @@ func (c *Client) ShowAppInstances(ctx context.Context, appInstKey AppInstanceKey if err != nil { return nil, fmt.Errorf("ShowAppInstances failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { return nil, c.handleErrorResponse(resp, "ShowAppInstances") @@ -125,7 +131,9 @@ func (c *Client) UpdateAppInstance(ctx context.Context, input *UpdateAppInstance if err != nil { return fmt.Errorf("UpdateAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "UpdateAppInstance") @@ -152,7 +160,9 @@ func (c *Client) RefreshAppInstance(ctx context.Context, appInstKey AppInstanceK if err != nil { return fmt.Errorf("RefreshAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "RefreshAppInstance") @@ -179,7 +189,9 @@ func (c *Client) DeleteAppInstance(ctx context.Context, appInstKey AppInstanceKe if err != nil { return fmt.Errorf("DeleteAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // 404 is acceptable for delete operations (already deleted) if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { diff --git a/sdk/edgeconnect/appinstance_test.go b/sdk/edgeconnect/appinstance_test.go index ac9c1eb..003f024 100644 --- a/sdk/edgeconnect/appinstance_test.go +++ b/sdk/edgeconnect/appinstance_test.go @@ -126,7 +126,7 @@ func TestCreateAppInstance(t *testing.T) { assert.Equal(t, "application/json", r.Header.Get("Content-Type")) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() @@ -207,7 +207,7 @@ func TestShowAppInstance(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() @@ -254,7 +254,7 @@ func TestShowAppInstances(t *testing.T) { {"data": {"key": {"organization": "testorg", "name": "inst2"}, "state": "Creating"}} ` w.WriteHeader(200) - w.Write([]byte(response)) + _, _ = w.Write([]byte(response)) })) defer server.Close() @@ -361,7 +361,7 @@ func TestUpdateAppInstance(t *testing.T) { assert.Equal(t, tt.input.AppInst.Key.Organization, input.AppInst.Key.Organization) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() diff --git a/sdk/edgeconnect/apps.go b/sdk/edgeconnect/apps.go index 8973862..f197a68 100644 --- a/sdk/edgeconnect/apps.go +++ b/sdk/edgeconnect/apps.go @@ -28,7 +28,9 @@ func (c *Client) CreateApp(ctx context.Context, input *NewAppInput) error { if err != nil { return fmt.Errorf("CreateApp failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "CreateApp") @@ -55,7 +57,9 @@ func (c *Client) ShowApp(ctx context.Context, appKey AppKey, region string) (App if err != nil { return App{}, fmt.Errorf("ShowApp failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return App{}, fmt.Errorf("app %s/%s version %s in region %s: %w", @@ -95,7 +99,9 @@ func (c *Client) ShowApps(ctx context.Context, appKey AppKey, region string) ([] if err != nil { return nil, fmt.Errorf("ShowApps failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { return nil, c.handleErrorResponse(resp, "ShowApps") @@ -124,7 +130,9 @@ func (c *Client) UpdateApp(ctx context.Context, input *UpdateAppInput) error { if err != nil { return fmt.Errorf("UpdateApp failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "UpdateApp") @@ -151,7 +159,9 @@ func (c *Client) DeleteApp(ctx context.Context, appKey AppKey, region string) er if err != nil { return fmt.Errorf("DeleteApp failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // 404 is acceptable for delete operations (already deleted) if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { @@ -238,7 +248,9 @@ func (c *Client) handleErrorResponse(resp *http.Response, operation string) erro bodyBytes := []byte{} if resp.Body != nil { - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() bodyBytes, _ = io.ReadAll(resp.Body) messages = append(messages, string(bodyBytes)) } diff --git a/sdk/edgeconnect/apps_test.go b/sdk/edgeconnect/apps_test.go index 30531f6..88437ca 100644 --- a/sdk/edgeconnect/apps_test.go +++ b/sdk/edgeconnect/apps_test.go @@ -67,7 +67,7 @@ func TestCreateApp(t *testing.T) { assert.Equal(t, "application/json", r.Header.Get("Content-Type")) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() @@ -139,7 +139,7 @@ func TestShowApp(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() @@ -186,7 +186,7 @@ func TestShowApps(t *testing.T) { {"data": {"key": {"organization": "testorg", "name": "app2", "version": "1.0.0"}, "deployment": "docker"}} ` w.WriteHeader(200) - w.Write([]byte(response)) + _, _ = w.Write([]byte(response)) })) defer server.Close() @@ -277,7 +277,7 @@ func TestUpdateApp(t *testing.T) { assert.Equal(t, tt.input.App.Key.Organization, input.App.Key.Organization) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() @@ -407,13 +407,3 @@ func TestAPIError(t *testing.T) { assert.Equal(t, 400, err.StatusCode) assert.Len(t, err.Messages, 2) } - -// Helper function to create a test server that handles streaming JSON responses -func createStreamingJSONServer(responses []string, statusCode int) *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(statusCode) - for _, response := range responses { - w.Write([]byte(response + "\n")) - } - })) -} diff --git a/sdk/edgeconnect/auth.go b/sdk/edgeconnect/auth.go index eab24b9..cf6067b 100644 --- a/sdk/edgeconnect/auth.go +++ b/sdk/edgeconnect/auth.go @@ -138,7 +138,9 @@ func (u *UsernamePasswordProvider) retrieveToken(ctx context.Context) (string, e if err != nil { return "", err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // Read response body - same as existing implementation body, err := io.ReadAll(resp.Body) diff --git a/sdk/edgeconnect/auth_test.go b/sdk/edgeconnect/auth_test.go index 8ea3176..8e68dc4 100644 --- a/sdk/edgeconnect/auth_test.go +++ b/sdk/edgeconnect/auth_test.go @@ -56,7 +56,7 @@ func TestUsernamePasswordProvider_Success(t *testing.T) { // Return token response := map[string]string{"token": "dynamic-token-456"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer loginServer.Close() @@ -75,7 +75,7 @@ func TestUsernamePasswordProvider_LoginFailure(t *testing.T) { // Mock login server that returns error loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Invalid credentials")) + _, _ = w.Write([]byte("Invalid credentials")) })) defer loginServer.Close() @@ -99,7 +99,7 @@ func TestUsernamePasswordProvider_TokenCaching(t *testing.T) { callCount++ response := map[string]string{"token": "cached-token-789"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer loginServer.Close() @@ -128,7 +128,7 @@ func TestUsernamePasswordProvider_TokenExpiry(t *testing.T) { callCount++ response := map[string]string{"token": "refreshed-token-999"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer loginServer.Close() @@ -157,7 +157,7 @@ func TestUsernamePasswordProvider_InvalidateToken(t *testing.T) { callCount++ response := map[string]string{"token": "new-token-after-invalidation"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer loginServer.Close() @@ -185,7 +185,7 @@ func TestUsernamePasswordProvider_BadJSONResponse(t *testing.T) { // Mock server returning invalid JSON loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte("invalid json response")) + _, _ = w.Write([]byte("invalid json response")) })) defer loginServer.Close() diff --git a/sdk/edgeconnect/cloudlet.go b/sdk/edgeconnect/cloudlet.go index 0ed6e71..142b9d6 100644 --- a/sdk/edgeconnect/cloudlet.go +++ b/sdk/edgeconnect/cloudlet.go @@ -22,7 +22,9 @@ func (c *Client) CreateCloudlet(ctx context.Context, input *NewCloudletInput) er if err != nil { return fmt.Errorf("CreateCloudlet failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "CreateCloudlet") @@ -49,7 +51,9 @@ func (c *Client) ShowCloudlet(ctx context.Context, cloudletKey CloudletKey, regi if err != nil { return Cloudlet{}, fmt.Errorf("ShowCloudlet failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return Cloudlet{}, fmt.Errorf("cloudlet %s/%s in region %s: %w", @@ -89,7 +93,9 @@ func (c *Client) ShowCloudlets(ctx context.Context, cloudletKey CloudletKey, reg if err != nil { return nil, fmt.Errorf("ShowCloudlets failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { return nil, c.handleErrorResponse(resp, "ShowCloudlets") @@ -123,7 +129,9 @@ func (c *Client) DeleteCloudlet(ctx context.Context, cloudletKey CloudletKey, re if err != nil { return fmt.Errorf("DeleteCloudlet failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // 404 is acceptable for delete operations (already deleted) if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { @@ -151,7 +159,9 @@ func (c *Client) GetCloudletManifest(ctx context.Context, cloudletKey CloudletKe if err != nil { return nil, fmt.Errorf("GetCloudletManifest failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return nil, fmt.Errorf("cloudlet manifest %s/%s in region %s: %w", @@ -189,7 +199,9 @@ func (c *Client) GetCloudletResourceUsage(ctx context.Context, cloudletKey Cloud if err != nil { return nil, fmt.Errorf("GetCloudletResourceUsage failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return nil, fmt.Errorf("cloudlet resource usage %s/%s in region %s: %w", diff --git a/sdk/edgeconnect/cloudlet_test.go b/sdk/edgeconnect/cloudlet_test.go index 7d129bb..b029f17 100644 --- a/sdk/edgeconnect/cloudlet_test.go +++ b/sdk/edgeconnect/cloudlet_test.go @@ -70,7 +70,7 @@ func TestCreateCloudlet(t *testing.T) { assert.Equal(t, "application/json", r.Header.Get("Content-Type")) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() @@ -140,7 +140,7 @@ func TestShowCloudlet(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() @@ -187,7 +187,7 @@ func TestShowCloudlets(t *testing.T) { {"data": {"key": {"organization": "cloudletorg", "name": "cloudlet2"}, "state": "Creating"}} ` w.WriteHeader(200) - w.Write([]byte(response)) + _, _ = w.Write([]byte(response)) })) defer server.Close() @@ -312,7 +312,7 @@ func TestGetCloudletManifest(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() @@ -380,7 +380,7 @@ func TestGetCloudletResourceUsage(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() diff --git a/sdk/edgeconnect/v2/appinstance.go b/sdk/edgeconnect/v2/appinstance.go index d38821e..f7b04bb 100644 --- a/sdk/edgeconnect/v2/appinstance.go +++ b/sdk/edgeconnect/v2/appinstance.go @@ -25,7 +25,9 @@ func (c *Client) CreateAppInstance(ctx context.Context, input *NewAppInstanceInp if err != nil { return fmt.Errorf("CreateAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "CreateAppInstance") @@ -58,7 +60,9 @@ func (c *Client) ShowAppInstance(ctx context.Context, appInstKey AppInstanceKey, if err != nil { return AppInstance{}, fmt.Errorf("ShowAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return AppInstance{}, fmt.Errorf("app instance %s/%s: %w", @@ -98,7 +102,9 @@ func (c *Client) ShowAppInstances(ctx context.Context, appInstKey AppInstanceKey if err != nil { return nil, fmt.Errorf("ShowAppInstances failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { return nil, c.handleErrorResponse(resp, "ShowAppInstances") @@ -127,7 +133,9 @@ func (c *Client) UpdateAppInstance(ctx context.Context, input *UpdateAppInstance if err != nil { return fmt.Errorf("UpdateAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "UpdateAppInstance") @@ -154,7 +162,9 @@ func (c *Client) RefreshAppInstance(ctx context.Context, appInstKey AppInstanceK if err != nil { return fmt.Errorf("RefreshAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "RefreshAppInstance") @@ -181,7 +191,9 @@ func (c *Client) DeleteAppInstance(ctx context.Context, appInstKey AppInstanceKe if err != nil { return fmt.Errorf("DeleteAppInstance failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // 404 is acceptable for delete operations (already deleted) if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { diff --git a/sdk/edgeconnect/v2/appinstance_test.go b/sdk/edgeconnect/v2/appinstance_test.go index e1c3d5e..dd0bc45 100644 --- a/sdk/edgeconnect/v2/appinstance_test.go +++ b/sdk/edgeconnect/v2/appinstance_test.go @@ -126,7 +126,7 @@ func TestCreateAppInstance(t *testing.T) { assert.Equal(t, "application/json", r.Header.Get("Content-Type")) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() @@ -207,7 +207,7 @@ func TestShowAppInstance(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() @@ -254,7 +254,7 @@ func TestShowAppInstances(t *testing.T) { {"data": {"key": {"organization": "testorg", "name": "inst2"}, "state": "Creating"}} ` w.WriteHeader(200) - w.Write([]byte(response)) + _, _ = w.Write([]byte(response)) })) defer server.Close() @@ -361,7 +361,7 @@ func TestUpdateAppInstance(t *testing.T) { assert.Equal(t, tt.input.AppInst.Key.Organization, input.AppInst.Key.Organization) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() diff --git a/sdk/edgeconnect/v2/apps.go b/sdk/edgeconnect/v2/apps.go index 8f5410e..80c3981 100644 --- a/sdk/edgeconnect/v2/apps.go +++ b/sdk/edgeconnect/v2/apps.go @@ -29,7 +29,9 @@ func (c *Client) CreateApp(ctx context.Context, input *NewAppInput) error { if err != nil { return fmt.Errorf("CreateApp failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "CreateApp") @@ -56,7 +58,9 @@ func (c *Client) ShowApp(ctx context.Context, appKey AppKey, region string) (App if err != nil { return App{}, fmt.Errorf("ShowApp failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return App{}, fmt.Errorf("app %s/%s version %s in region %s: %w", @@ -96,7 +100,9 @@ func (c *Client) ShowApps(ctx context.Context, appKey AppKey, region string) ([] if err != nil { return nil, fmt.Errorf("ShowApps failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { return nil, c.handleErrorResponse(resp, "ShowApps") @@ -125,7 +131,9 @@ func (c *Client) UpdateApp(ctx context.Context, input *UpdateAppInput) error { if err != nil { return fmt.Errorf("UpdateApp failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "UpdateApp") @@ -152,7 +160,9 @@ func (c *Client) DeleteApp(ctx context.Context, appKey AppKey, region string) er if err != nil { return fmt.Errorf("DeleteApp failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // 404 is acceptable for delete operations (already deleted) if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { @@ -254,7 +264,9 @@ func (c *Client) handleErrorResponse(resp *http.Response, operation string) erro bodyBytes := []byte{} if resp.Body != nil { - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() bodyBytes, _ = io.ReadAll(resp.Body) messages = append(messages, string(bodyBytes)) } diff --git a/sdk/edgeconnect/v2/apps_test.go b/sdk/edgeconnect/v2/apps_test.go index 4ea757c..a4c202f 100644 --- a/sdk/edgeconnect/v2/apps_test.go +++ b/sdk/edgeconnect/v2/apps_test.go @@ -67,7 +67,7 @@ func TestCreateApp(t *testing.T) { assert.Equal(t, "application/json", r.Header.Get("Content-Type")) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() @@ -139,7 +139,7 @@ func TestShowApp(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() @@ -186,7 +186,7 @@ func TestShowApps(t *testing.T) { {"data": {"key": {"organization": "testorg", "name": "app2", "version": "1.0.0"}, "deployment": "docker"}} ` w.WriteHeader(200) - w.Write([]byte(response)) + _, _ = w.Write([]byte(response)) })) defer server.Close() @@ -277,7 +277,7 @@ func TestUpdateApp(t *testing.T) { assert.Equal(t, tt.input.App.Key.Organization, input.App.Key.Organization) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() @@ -407,13 +407,3 @@ func TestAPIError(t *testing.T) { assert.Equal(t, 400, err.StatusCode) assert.Len(t, err.Messages, 2) } - -// Helper function to create a test server that handles streaming JSON responses -func createStreamingJSONServer(responses []string, statusCode int) *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(statusCode) - for _, response := range responses { - w.Write([]byte(response + "\n")) - } - })) -} diff --git a/sdk/edgeconnect/v2/auth.go b/sdk/edgeconnect/v2/auth.go index a1f33a2..f428f64 100644 --- a/sdk/edgeconnect/v2/auth.go +++ b/sdk/edgeconnect/v2/auth.go @@ -138,7 +138,9 @@ func (u *UsernamePasswordProvider) retrieveToken(ctx context.Context) (string, e if err != nil { return "", err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // Read response body - same as existing implementation body, err := io.ReadAll(resp.Body) diff --git a/sdk/edgeconnect/v2/auth_test.go b/sdk/edgeconnect/v2/auth_test.go index 0fc5b24..34ebcaf 100644 --- a/sdk/edgeconnect/v2/auth_test.go +++ b/sdk/edgeconnect/v2/auth_test.go @@ -56,7 +56,7 @@ func TestUsernamePasswordProvider_Success(t *testing.T) { // Return token response := map[string]string{"token": "dynamic-token-456"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer loginServer.Close() @@ -75,7 +75,7 @@ func TestUsernamePasswordProvider_LoginFailure(t *testing.T) { // Mock login server that returns error loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Invalid credentials")) + _, _ = w.Write([]byte("Invalid credentials")) })) defer loginServer.Close() @@ -99,7 +99,7 @@ func TestUsernamePasswordProvider_TokenCaching(t *testing.T) { callCount++ response := map[string]string{"token": "cached-token-789"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer loginServer.Close() @@ -128,7 +128,7 @@ func TestUsernamePasswordProvider_TokenExpiry(t *testing.T) { callCount++ response := map[string]string{"token": "refreshed-token-999"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer loginServer.Close() @@ -157,7 +157,7 @@ func TestUsernamePasswordProvider_InvalidateToken(t *testing.T) { callCount++ response := map[string]string{"token": "new-token-after-invalidation"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer loginServer.Close() @@ -185,7 +185,7 @@ func TestUsernamePasswordProvider_BadJSONResponse(t *testing.T) { // Mock server returning invalid JSON loginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte("invalid json response")) + _, _ = w.Write([]byte("invalid json response")) })) defer loginServer.Close() diff --git a/sdk/edgeconnect/v2/cloudlet.go b/sdk/edgeconnect/v2/cloudlet.go index 415584a..c877486 100644 --- a/sdk/edgeconnect/v2/cloudlet.go +++ b/sdk/edgeconnect/v2/cloudlet.go @@ -22,7 +22,9 @@ func (c *Client) CreateCloudlet(ctx context.Context, input *NewCloudletInput) er if err != nil { return fmt.Errorf("CreateCloudlet failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 { return c.handleErrorResponse(resp, "CreateCloudlet") @@ -49,7 +51,9 @@ func (c *Client) ShowCloudlet(ctx context.Context, cloudletKey CloudletKey, regi if err != nil { return Cloudlet{}, fmt.Errorf("ShowCloudlet failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return Cloudlet{}, fmt.Errorf("cloudlet %s/%s in region %s: %w", @@ -89,7 +93,9 @@ func (c *Client) ShowCloudlets(ctx context.Context, cloudletKey CloudletKey, reg if err != nil { return nil, fmt.Errorf("ShowCloudlets failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { return nil, c.handleErrorResponse(resp, "ShowCloudlets") @@ -123,7 +129,9 @@ func (c *Client) DeleteCloudlet(ctx context.Context, cloudletKey CloudletKey, re if err != nil { return fmt.Errorf("DeleteCloudlet failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // 404 is acceptable for delete operations (already deleted) if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound { @@ -151,7 +159,9 @@ func (c *Client) GetCloudletManifest(ctx context.Context, cloudletKey CloudletKe if err != nil { return nil, fmt.Errorf("GetCloudletManifest failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return nil, fmt.Errorf("cloudlet manifest %s/%s in region %s: %w", @@ -189,7 +199,9 @@ func (c *Client) GetCloudletResourceUsage(ctx context.Context, cloudletKey Cloud if err != nil { return nil, fmt.Errorf("GetCloudletResourceUsage failed: %w", err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if resp.StatusCode == http.StatusNotFound { return nil, fmt.Errorf("cloudlet resource usage %s/%s in region %s: %w", diff --git a/sdk/edgeconnect/v2/cloudlet_test.go b/sdk/edgeconnect/v2/cloudlet_test.go index 8f2cc06..d8ffb75 100644 --- a/sdk/edgeconnect/v2/cloudlet_test.go +++ b/sdk/edgeconnect/v2/cloudlet_test.go @@ -70,7 +70,7 @@ func TestCreateCloudlet(t *testing.T) { assert.Equal(t, "application/json", r.Header.Get("Content-Type")) w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) })) defer server.Close() @@ -140,7 +140,7 @@ func TestShowCloudlet(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() @@ -187,7 +187,7 @@ func TestShowCloudlets(t *testing.T) { {"data": {"key": {"organization": "cloudletorg", "name": "cloudlet2"}, "state": "Creating"}} ` w.WriteHeader(200) - w.Write([]byte(response)) + _, _ = w.Write([]byte(response)) })) defer server.Close() @@ -312,7 +312,7 @@ func TestGetCloudletManifest(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() @@ -380,7 +380,7 @@ func TestGetCloudletResourceUsage(t *testing.T) { w.WriteHeader(tt.mockStatusCode) if tt.mockResponse != "" { - w.Write([]byte(tt.mockResponse)) + _, _ = w.Write([]byte(tt.mockResponse)) } })) defer server.Close() diff --git a/sdk/internal/http/transport.go b/sdk/internal/http/transport.go index c3bbab1..35b71b8 100644 --- a/sdk/internal/http/transport.go +++ b/sdk/internal/http/transport.go @@ -162,7 +162,9 @@ func (t *Transport) CallJSON(ctx context.Context, method, url string, body inter if err != nil { return resp, err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // Read response body respBody, err := io.ReadAll(resp.Body)