From 31bdb948a88227dc75634ef6b5ecf901a4ec4572 Mon Sep 17 00:00:00 2001 From: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Date: Wed, 10 Jul 2024 19:22:47 -0300 Subject: [PATCH] chore(code): use int ranges in loops BE-10990 (#12028) --- .golangci.yaml | 1 + api/apikey/apikey_test.go | 4 +- api/concurrent/concurrent.go | 6 +- api/dataservices/stack/tests/stack_test.go | 2 +- api/docker/images/registry.go | 51 +++--- .../customtemplate_git_fetch_test.go | 9 +- .../handler/endpoints/endpoint_delete_test.go | 2 +- api/http/handler/tags/tag_delete_test.go | 2 +- .../factory/kubernetes/token_cache_test.go | 2 +- api/internal/endpointutils/endpointutils.go | 57 +++--- api/kubernetes/cli/rbac.go | 48 +++--- .../compose_unpacker_cmd_builder.go | 162 +++++++++--------- 12 files changed, 180 insertions(+), 166 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 1687a54f2..3be4f7e11 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -10,6 +10,7 @@ linters: - govet - errorlint - copyloopvar + - intrange linters-settings: depguard: diff --git a/api/apikey/apikey_test.go b/api/apikey/apikey_test.go index ace83d62d..a11a08237 100644 --- a/api/apikey/apikey_test.go +++ b/api/apikey/apikey_test.go @@ -40,10 +40,12 @@ func Test_generateRandomKey(t *testing.T) { t.Run("Generated keys are unique", func(t *testing.T) { keys := make(map[string]bool) - for i := 0; i < 100; i++ { + + for range 100 { key := GenerateRandomKey(8) _, ok := keys[string(key)] is.False(ok) + keys[string(key)] = true } }) diff --git a/api/concurrent/concurrent.go b/api/concurrent/concurrent.go index 1695fa802..e588732b3 100644 --- a/api/concurrent/concurrent.go +++ b/api/concurrent/concurrent.go @@ -93,6 +93,7 @@ type Func func(ctx context.Context) (any, error) // Run runs a list of functions returns the results func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, error) { var wg sync.WaitGroup + resultsChan := make(chan Result, len(tasks)) taskChan := make(chan Func, len(tasks)) @@ -101,6 +102,7 @@ func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, erro runTask := func() { defer wg.Done() + for fn := range taskChan { result, err := fn(localCtx) resultsChan <- Result{Result: result, Err: err} @@ -113,7 +115,7 @@ func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, erro } // Start worker goroutines - for i := 0; i < maxConcurrency; i++ { + for range maxConcurrency { wg.Add(1) go runTask() } @@ -135,8 +137,10 @@ func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, erro for r := range resultsChan { if r.Err != nil { cancelCtx() + return nil, r.Err } + results = append(results, r) } diff --git a/api/dataservices/stack/tests/stack_test.go b/api/dataservices/stack/tests/stack_test.go index 0929c3087..a2bf83635 100644 --- a/api/dataservices/stack/tests/stack_test.go +++ b/api/dataservices/stack/tests/stack_test.go @@ -33,7 +33,7 @@ func TestService_StackByWebhookID(t *testing.T) { b := stackBuilder{t: t, store: store} b.createNewStack(newGuidString(t)) - for i := 0; i < 10; i++ { + for range 10 { b.createNewStack("") } webhookID := newGuidString(t) diff --git a/api/docker/images/registry.go b/api/docker/images/registry.go index 6808dd313..10b4a6388 100644 --- a/api/docker/images/registry.go +++ b/api/docker/images/registry.go @@ -1,6 +1,7 @@ package images import ( + "cmp" "strings" "time" @@ -41,8 +42,7 @@ func (c *RegistryClient) RegistryAuth(image Image) (string, string, error) { } func (c *RegistryClient) CertainRegistryAuth(registry *portainer.Registry) (string, string, error) { - err := registryutils.EnsureRegTokenValid(c.dataStore, registry) - if err != nil { + if err := registryutils.EnsureRegTokenValid(c.dataStore, registry); err != nil { return "", "", err } @@ -72,8 +72,7 @@ func (c *RegistryClient) EncodedRegistryAuth(image Image) (string, error) { } func (c *RegistryClient) EncodedCertainRegistryAuth(registry *portainer.Registry) (string, error) { - err := registryutils.EnsureRegTokenValid(c.dataStore, registry) - if err != nil { + if err := registryutils.EnsureRegTokenValid(c.dataStore, registry); err != nil { return "", err } @@ -92,38 +91,32 @@ func findBestMatchRegistry(repository string, registries []portainer.Registry) ( } var match1, match2, match3 *portainer.Registry - for i := 0; i < len(registries); i++ { - registry := registries[i] - if registry.Type == portainer.DockerHubRegistry { - - // try to match repository examples: - // /nginx:latest - // docker.io//nginx:latest - if strings.HasPrefix(repository, registry.Username+"/") || strings.HasPrefix(repository, registry.URL+"/"+registry.Username+"/") { - match1 = ®istry - } - - // try to match repository examples: - // portainer/portainer-ee:latest - // /portainer-ee:latest - if match3 == nil { - match3 = ®istry - } - } + for _, registry := range registries { if strings.Contains(repository, registry.URL) { match2 = ®istry } - } - match := match1 - if match == nil { - match = match2 - } - if match == nil { - match = match3 + if registry.Type != portainer.DockerHubRegistry { + continue + } + + // try to match repository examples: + // /nginx:latest + // docker.io//nginx:latest + if strings.HasPrefix(repository, registry.Username+"/") || strings.HasPrefix(repository, registry.URL+"/"+registry.Username+"/") { + match1 = ®istry + } + + // try to match repository examples: + // portainer/portainer-ee:latest + // /portainer-ee:latest + if match3 == nil { + match3 = ®istry + } } + match := cmp.Or(match1, match2, match3) if match == nil { return nil, errors.New("no registries matched") } diff --git a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go index f574a0c9b..60ed1666f 100644 --- a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go +++ b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go @@ -157,28 +157,33 @@ func Test_customTemplateGitFetch(t *testing.T) { t.Run("can return the expected file content by multiple calls from one user", func(t *testing.T) { var wg sync.WaitGroup wg.Add(5) - for i := 0; i < 5; i++ { + + for range 5 { go func() { singleAPIRequest(h, jwt1, is, "abcdefg") wg.Done() }() } + wg.Wait() }) t.Run("can return the expected file content by multiple calls from different users", func(t *testing.T) { var wg sync.WaitGroup wg.Add(10) - for i := 0; i < 10; i++ { + + for i := range 10 { go func(j int) { if j%2 == 0 { singleAPIRequest(h, jwt1, is, "abcdefg") } else { singleAPIRequest(h, jwt2, is, "abcdefg") } + wg.Done() }(i) } + wg.Wait() }) diff --git a/api/http/handler/endpoints/endpoint_delete_test.go b/api/http/handler/endpoints/endpoint_delete_test.go index 4b9034a31..309b45ffe 100644 --- a/api/http/handler/endpoints/endpoint_delete_test.go +++ b/api/http/handler/endpoints/endpoint_delete_test.go @@ -27,7 +27,7 @@ func TestEndpointDeleteEdgeGroupsConcurrently(t *testing.T) { var endpointIDs []portainer.EndpointID - for i := 0; i < endpointsCount; i++ { + for i := range endpointsCount { endpointID := portainer.EndpointID(i) + 1 if err := store.Endpoint().Create(&portainer.Endpoint{ diff --git a/api/http/handler/tags/tag_delete_test.go b/api/http/handler/tags/tag_delete_test.go index 3c55ac509..cabf20963 100644 --- a/api/http/handler/tags/tag_delete_test.go +++ b/api/http/handler/tags/tag_delete_test.go @@ -29,7 +29,7 @@ func TestTagDeleteEdgeGroupsConcurrently(t *testing.T) { var tagIDs []portainer.TagID - for i := 0; i < tagsCount; i++ { + for i := range tagsCount { tagID := portainer.TagID(i) + 1 if err := store.Tag().Create(&portainer.Tag{ diff --git a/api/http/proxy/factory/kubernetes/token_cache_test.go b/api/http/proxy/factory/kubernetes/token_cache_test.go index 525576d64..972421be2 100644 --- a/api/http/proxy/factory/kubernetes/token_cache_test.go +++ b/api/http/proxy/factory/kubernetes/token_cache_test.go @@ -27,7 +27,7 @@ func failFunc(t *testing.T) func() (string, error) { func TestTokenCacheDataRace(t *testing.T) { ch := make(chan struct{}) - for i := 0; i < 1000; i++ { + for range 1000 { var tokenCache1, tokenCache2 *tokenCache mgr := NewTokenCacheManager() diff --git a/api/internal/endpointutils/endpointutils.go b/api/internal/endpointutils/endpointutils.go index 269ed5aa8..ca9f34ad1 100644 --- a/api/internal/endpointutils/endpointutils.go +++ b/api/internal/endpointutils/endpointutils.go @@ -13,7 +13,9 @@ import ( // IsLocalEndpoint returns true if this is a local environment(endpoint) func IsLocalEndpoint(endpoint *portainer.Endpoint) bool { - return strings.HasPrefix(endpoint.URL, "unix://") || strings.HasPrefix(endpoint.URL, "npipe://") || endpoint.Type == 5 + return strings.HasPrefix(endpoint.URL, "unix://") || + strings.HasPrefix(endpoint.URL, "npipe://") || + endpoint.Type == portainer.KubernetesLocalEnvironment } // IsKubernetesEndpoint returns true if this is a kubernetes environment(endpoint) @@ -61,6 +63,7 @@ func FilterByExcludeIDs(endpoints []portainer.Endpoint, excludeIds []portainer.E filteredEndpoints = append(filteredEndpoints, endpoint) } } + return filteredEndpoints } @@ -187,12 +190,15 @@ func InitialStorageDetection(endpoint *portainer.Endpoint, endpointService datas endpoint, ) }() + log.Info().Msg("attempting to detect storage classes in the cluster") + err := storageDetect(endpoint, endpointService, factory) if err == nil { return } log.Err(err).Msg("error while detecting storage classes") + go func() { // Retry after 30 seconds if the initial detection failed. log.Info().Msg("retrying storage detection in 30 seconds") @@ -203,38 +209,41 @@ func InitialStorageDetection(endpoint *portainer.Endpoint, endpointService datas } func UpdateEdgeEndpointHeartbeat(endpoint *portainer.Endpoint, settings *portainer.Settings) { - if IsEdgeEndpoint(endpoint) { - endpoint.QueryDate = time.Now().Unix() - checkInInterval := getEndpointCheckinInterval(endpoint, settings) - endpoint.Heartbeat = endpoint.QueryDate-endpoint.LastCheckInDate <= int64(checkInInterval*2+20) + if !IsEdgeEndpoint(endpoint) { + return } + + endpoint.QueryDate = time.Now().Unix() + checkInInterval := getEndpointCheckinInterval(endpoint, settings) + endpoint.Heartbeat = endpoint.QueryDate-endpoint.LastCheckInDate <= int64(checkInInterval*2+20) } func getEndpointCheckinInterval(endpoint *portainer.Endpoint, settings *portainer.Settings) int { - if endpoint.Edge.AsyncMode { - defaultInterval := 60 - intervals := [][]int{ - {endpoint.Edge.PingInterval, settings.Edge.PingInterval}, - {endpoint.Edge.CommandInterval, settings.Edge.CommandInterval}, - {endpoint.Edge.SnapshotInterval, settings.Edge.SnapshotInterval}, + if !endpoint.Edge.AsyncMode { + if endpoint.EdgeCheckinInterval > 0 { + return endpoint.EdgeCheckinInterval } - for i := 0; i < len(intervals); i++ { - effectiveInterval := intervals[i][0] - if effectiveInterval <= 0 { - effectiveInterval = intervals[i][1] - } - if effectiveInterval > 0 && effectiveInterval < defaultInterval { - defaultInterval = effectiveInterval - } - } + return settings.EdgeAgentCheckinInterval + } - return defaultInterval + defaultInterval := 60 + intervals := [][]int{ + {endpoint.Edge.PingInterval, settings.Edge.PingInterval}, + {endpoint.Edge.CommandInterval, settings.Edge.CommandInterval}, + {endpoint.Edge.SnapshotInterval, settings.Edge.SnapshotInterval}, } - if endpoint.EdgeCheckinInterval > 0 { - return endpoint.EdgeCheckinInterval + for i := range len(intervals) { + effectiveInterval := intervals[i][0] + if effectiveInterval <= 0 { + effectiveInterval = intervals[i][1] + } + + if effectiveInterval > 0 && effectiveInterval < defaultInterval { + defaultInterval = effectiveInterval + } } - return settings.EdgeAgentCheckinInterval + return defaultInterval } diff --git a/api/kubernetes/cli/rbac.go b/api/kubernetes/cli/rbac.go index 9758812d1..17472820f 100644 --- a/api/kubernetes/cli/rbac.go +++ b/api/kubernetes/cli/rbac.go @@ -16,6 +16,8 @@ import ( rbacv1types "k8s.io/client-go/kubernetes/typed/rbac/v1" ) +const maxRetries = 5 + // IsRBACEnabled checks if RBAC is enabled in the cluster by creating a service account, then checking it's access to a resourcequota before and after setting a cluster role and cluster role binding func (kcl *KubeClient) IsRBACEnabled() (bool, error) { namespace := "default" @@ -23,11 +25,11 @@ func (kcl *KubeClient) IsRBACEnabled() (bool, error) { resource := "resourcequotas" saClient := kcl.cli.CoreV1().ServiceAccounts(namespace) - uniqueString := randomstring.RandomString(4) // append a unique string to resource names, incase they already exist + uniqueString := randomstring.RandomString(4) // Append a unique string to resource names, in case they already exist saName := "portainer-rbac-test-sa-" + uniqueString - err := createServiceAccount(saClient, saName, namespace) - if err != nil { + if err := createServiceAccount(saClient, saName, namespace); err != nil { log.Error().Err(err).Msg("Error creating service account") + return false, err } defer deleteServiceAccount(saClient, saName) @@ -36,29 +38,30 @@ func (kcl *KubeClient) IsRBACEnabled() (bool, error) { allowed, err := checkServiceAccountAccess(accessReviewClient, saName, verb, resource, namespace) if err != nil { log.Error().Err(err).Msg("Error checking service account access") + return false, err } - // if the service account with no authorizations is allowed, RBAC must be disabled + // If the service account with no authorizations is allowed, RBAC must be disabled if allowed { return false, nil } - // otherwise give the service account an rbac authorisation and check again + // Otherwise give the service account an rbac authorisation and check again roleClient := kcl.cli.RbacV1().Roles(namespace) roleName := "portainer-rbac-test-role-" + uniqueString - err = createRole(roleClient, roleName, verb, resource, namespace) - if err != nil { + if err := createRole(roleClient, roleName, verb, resource, namespace); err != nil { log.Error().Err(err).Msg("Error creating role") + return false, err } defer deleteRole(roleClient, roleName) roleBindingClient := kcl.cli.RbacV1().RoleBindings(namespace) roleBindingName := "portainer-rbac-test-role-binding-" + uniqueString - err = createRoleBinding(roleBindingClient, roleBindingName, roleName, saName, namespace) - if err != nil { + if err := createRoleBinding(roleBindingClient, roleBindingName, roleName, saName, namespace); err != nil { log.Error().Err(err).Msg("Error creating role binding") + return false, err } defer deleteRoleBinding(roleBindingClient, roleBindingName) @@ -66,10 +69,11 @@ func (kcl *KubeClient) IsRBACEnabled() (bool, error) { allowed, err = checkServiceAccountAccess(accessReviewClient, saName, verb, resource, namespace) if err != nil { log.Error().Err(err).Msg("Error checking service account access with authorizations added") + return false, err } - // if the service account allowed to list resource quotas after given rbac role, then RBAC is enabled + // If the service account allowed to list resource quotas after given rbac role, then RBAC is enabled return allowed, nil } @@ -80,13 +84,13 @@ func createServiceAccount(saClient corev1types.ServiceAccountInterface, name str Namespace: namespace, }, } + _, err := saClient.Create(context.Background(), serviceAccount, metav1.CreateOptions{}) return err } func deleteServiceAccount(saClient corev1types.ServiceAccountInterface, name string) { - err := saClient.Delete(context.Background(), name, metav1.DeleteOptions{}) - if err != nil { + if err := saClient.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil { log.Error().Err(err).Msg("Error deleting service account: " + name) } } @@ -105,13 +109,13 @@ func createRole(roleClient rbacv1types.RoleInterface, name string, verb string, }, }, } + _, err := roleClient.Create(context.Background(), role, metav1.CreateOptions{}) return err } func deleteRole(roleClient rbacv1types.RoleInterface, name string) { - err := roleClient.Delete(context.Background(), name, metav1.DeleteOptions{}) - if err != nil { + if err := roleClient.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil { log.Error().Err(err).Msg("Error deleting role: " + name) } } @@ -134,17 +138,19 @@ func createRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, clust APIGroup: "rbac.authorization.k8s.io", }, } + roleBinding, err := roleBindingClient.Create(context.Background(), clusterRoleBinding, metav1.CreateOptions{}) if err != nil { log.Error().Err(err).Msg("Error creating role binding: " + clusterRoleBindingName) + return err } // Retry checkRoleBinding a maximum of 5 times with a 100ms wait after each attempt - maxRetries := 5 - for i := 0; i < maxRetries; i++ { + for range maxRetries { err = checkRoleBinding(roleBindingClient, roleBinding.Name) time.Sleep(100 * time.Millisecond) // Wait for 100ms, even if the check passes + if err == nil { break } @@ -154,17 +160,17 @@ func createRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, clust } func checkRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, name string) error { - _, err := roleBindingClient.Get(context.Background(), name, metav1.GetOptions{}) - if err != nil { + if _, err := roleBindingClient.Get(context.Background(), name, metav1.GetOptions{}); err != nil { log.Error().Err(err).Msg("Error finding rolebinding: " + name) + return err } + return nil } func deleteRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, name string) { - err := roleBindingClient.Delete(context.Background(), name, metav1.DeleteOptions{}) - if err != nil { + if err := roleBindingClient.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil { log.Error().Err(err).Msg("Error deleting role binding: " + name) } } @@ -183,9 +189,11 @@ func checkServiceAccountAccess(accessReviewClient authv1types.LocalSubjectAccess User: "system:serviceaccount:default:" + serviceAccountName, // a workaround to be able to use the service account as a user }, } + result, err := accessReviewClient.Create(context.Background(), subjectAccessReview, metav1.CreateOptions{}) if err != nil { return false, err } + return result.Status.Allowed, nil } diff --git a/api/stacks/deployments/compose_unpacker_cmd_builder.go b/api/stacks/deployments/compose_unpacker_cmd_builder.go index 20bf56d43..734e548f2 100644 --- a/api/stacks/deployments/compose_unpacker_cmd_builder.go +++ b/api/stacks/deployments/compose_unpacker_cmd_builder.go @@ -50,13 +50,12 @@ var funcmap = map[StackRemoteOperation]buildCmdFunc{ // build the unpacker cmd for stack based on stackOperation func (d *stackDeployer) buildUnpackerCmdForStack(stack *portainer.Stack, operation StackRemoteOperation, opts unpackerCmdBuilderOptions) ([]string, error) { - fn := funcmap[operation] if fn == nil { return nil, fmt.Errorf("unknown stack operation %s", operation) } - registriesStrings := getRegistry(opts.registries, d.dataStore) + registriesStrings := generateRegistriesStrings(opts.registries, d.dataStore) envStrings := getEnv(stack.Env) return fn(stack, opts, registriesStrings, envStrings), nil @@ -64,72 +63,74 @@ func (d *stackDeployer) buildUnpackerCmdForStack(stack *portainer.Stack, operati // deploy [-u username -p password] [--skip-tls-verify] [--env KEY1=VALUE1 --env KEY2=VALUE2] [...] func buildDeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { - cmd := []string{} - cmd = append(cmd, UnpackerCmdDeploy) + cmd := []string{UnpackerCmdDeploy} cmd = appendGitAuthIfNeeded(cmd, stack) cmd = appendSkipTLSVerifyIfNeeded(cmd, stack) cmd = append(cmd, env...) cmd = append(cmd, registries...) - cmd = append(cmd, stack.GitConfig.URL) - cmd = append(cmd, stack.GitConfig.ReferenceName) - cmd = append(cmd, stack.Name) - cmd = append(cmd, opts.composeDestination) - cmd = append(cmd, stack.EntryPoint) - cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) - return cmd + cmd = append(cmd, + stack.GitConfig.URL, + stack.GitConfig.ReferenceName, + stack.Name, + opts.composeDestination, + stack.EntryPoint, + ) + + return append(cmd, stack.AdditionalFiles...) } // undeploy [-u username -p password] [-k] [...] func buildUndeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { - cmd := []string{} - cmd = append(cmd, UnpackerCmdUndeploy) + cmd := []string{UnpackerCmdUndeploy} cmd = appendGitAuthIfNeeded(cmd, stack) - cmd = append(cmd, stack.GitConfig.URL) - cmd = append(cmd, stack.Name) - cmd = append(cmd, opts.composeDestination) - cmd = append(cmd, stack.EntryPoint) - cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) - return cmd + cmd = append(cmd, stack.GitConfig.URL, + stack.Name, + opts.composeDestination, + stack.EntryPoint, + ) + + return append(cmd, stack.AdditionalFiles...) } // deploy [-u username -p password] [--skip-tls-verify] [-k] [--env KEY1=VALUE1 --env KEY2=VALUE2] [...] func buildComposeStartCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { - cmd := []string{} - cmd = append(cmd, UnpackerCmdDeploy) + cmd := []string{UnpackerCmdDeploy} cmd = appendGitAuthIfNeeded(cmd, stack) cmd = appendSkipTLSVerifyIfNeeded(cmd, stack) cmd = append(cmd, "-k") cmd = append(cmd, env...) cmd = append(cmd, registries...) - cmd = append(cmd, stack.GitConfig.URL) - cmd = append(cmd, stack.GitConfig.ReferenceName) - cmd = append(cmd, stack.Name) - cmd = append(cmd, opts.composeDestination) - cmd = append(cmd, stack.EntryPoint) - cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) - return cmd + cmd = append(cmd, stack.GitConfig.URL, + stack.GitConfig.ReferenceName, + stack.Name, + opts.composeDestination, + stack.EntryPoint, + ) + + return append(cmd, stack.AdditionalFiles...) } // undeploy [-u username -p password] [-k] [...] func buildComposeStopCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { - cmd := []string{} - cmd = append(cmd, UnpackerCmdUndeploy) + cmd := []string{UnpackerCmdUndeploy} cmd = appendGitAuthIfNeeded(cmd, stack) - cmd = append(cmd, "-k") - cmd = append(cmd, stack.GitConfig.URL) - cmd = append(cmd, stack.Name) - cmd = append(cmd, opts.composeDestination) - cmd = append(cmd, stack.EntryPoint) - cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) - return cmd + cmd = append(cmd, + "-k", + stack.GitConfig.URL, + stack.Name, + opts.composeDestination, + stack.EntryPoint, + ) + + return append(cmd, stack.AdditionalFiles...) } // swarm-deploy [-u username -p password] [--skip-tls-verify] [-f] [-r] [--env KEY1=VALUE1 --env KEY2=VALUE2] [...] func buildSwarmDeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { - cmd := []string{} - cmd = append(cmd, UnpackerCmdSwarmDeploy) + cmd := []string{UnpackerCmdSwarmDeploy} cmd = appendGitAuthIfNeeded(cmd, stack) cmd = appendSkipTLSVerifyIfNeeded(cmd, stack) + if opts.pullImage { cmd = append(cmd, "-f") } @@ -137,85 +138,76 @@ func buildSwarmDeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, if opts.prune { cmd = append(cmd, "-r") } + cmd = append(cmd, env...) cmd = append(cmd, registries...) - cmd = append(cmd, stack.GitConfig.URL) - cmd = append(cmd, stack.GitConfig.ReferenceName) - cmd = append(cmd, stack.Name) - cmd = append(cmd, opts.composeDestination) - cmd = append(cmd, stack.EntryPoint) - cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) - return cmd + cmd = append(cmd, stack.GitConfig.URL, + stack.GitConfig.ReferenceName, + stack.Name, + opts.composeDestination, + stack.EntryPoint, + ) + + return append(cmd, stack.AdditionalFiles...) } // swarm-undeploy [-k] func buildSwarmUndeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { - cmd := []string{} - cmd = append(cmd, UnpackerCmdSwarmUndeploy) - cmd = append(cmd, stack.Name) - cmd = append(cmd, opts.composeDestination) - return cmd + return []string{UnpackerCmdSwarmUndeploy, stack.Name, opts.composeDestination} } // swarm-deploy [-u username -p password] [-f] [-r] [-k] [--skip-tls-verify] [--env KEY1=VALUE1 --env KEY2=VALUE2] [...] func buildSwarmStartCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { - cmd := []string{} - cmd = append(cmd, UnpackerCmdSwarmDeploy, "-f", "-r", "-k") + cmd := []string{UnpackerCmdSwarmDeploy, "-f", "-r", "-k"} cmd = appendSkipTLSVerifyIfNeeded(cmd, stack) cmd = append(cmd, getEnv(stack.Env)...) cmd = append(cmd, registries...) - cmd = append(cmd, stack.GitConfig.URL) - cmd = append(cmd, stack.GitConfig.ReferenceName) - cmd = append(cmd, stack.Name) - cmd = append(cmd, opts.composeDestination) - cmd = append(cmd, stack.EntryPoint) - cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) - return cmd + cmd = append(cmd, stack.GitConfig.URL, + stack.GitConfig.ReferenceName, + stack.Name, + opts.composeDestination, + stack.EntryPoint, + ) + + return append(cmd, stack.AdditionalFiles...) } // swarm-undeploy [-k] func buildSwarmStopCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { - cmd := []string{} - cmd = append(cmd, UnpackerCmdSwarmUndeploy, "-k") - cmd = append(cmd, stack.Name) - cmd = append(cmd, opts.composeDestination) - return cmd + return []string{UnpackerCmdSwarmUndeploy, "-k", stack.Name, opts.composeDestination} } func appendGitAuthIfNeeded(cmd []string, stack *portainer.Stack) []string { - if stack.GitConfig.Authentication != nil && len(stack.GitConfig.Authentication.Password) != 0 { - cmd = append(cmd, "-u", stack.GitConfig.Authentication.Username, "-p", stack.GitConfig.Authentication.Password) + if stack.GitConfig.Authentication == nil || stack.GitConfig.Authentication.Password == "" { + return cmd } - return cmd + + return append(cmd, "-u", stack.GitConfig.Authentication.Username, "-p", stack.GitConfig.Authentication.Password) } func appendSkipTLSVerifyIfNeeded(cmd []string, stack *portainer.Stack) []string { - if stack.GitConfig.TLSSkipVerify { - cmd = append(cmd, "--skip-tls-verify") + if !stack.GitConfig.TLSSkipVerify { + return cmd } - return cmd -} -func appendAdditionalFiles(cmd []string, files []string) []string { - for i := 0; i < len(files); i++ { - cmd = append(cmd, files[i]) - } - return cmd + return append(cmd, "--skip-tls-verify") } -func getRegistry(registries []portainer.Registry, dataStore dataservices.DataStore) []string { +func generateRegistriesStrings(registries []portainer.Registry, dataStore dataservices.DataStore) []string { cmds := []string{} for _, registry := range registries { if registry.Authentication { - err := registryutils.EnsureRegTokenValid(dataStore, ®istry) - if err == nil { - username, password, err := registryutils.GetRegEffectiveCredential(®istry) - if err == nil { - cmd := fmt.Sprintf("--registry=%s:%s:%s", username, password, registry.URL) - cmds = append(cmds, cmd) - } + if err := registryutils.EnsureRegTokenValid(dataStore, ®istry); err != nil { + continue + } + + username, password, err := registryutils.GetRegEffectiveCredential(®istry) + if err != nil { + continue } + + cmds = append(cmds, fmt.Sprintf("--registry=%s:%s:%s", username, password, registry.URL)) } }