chore(code): use int ranges in loops BE-10990 (#12028)

pull/10001/merge
andres-portainer 5 months ago committed by GitHub
parent 468c12c75b
commit 31bdb948a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -10,6 +10,7 @@ linters:
- govet - govet
- errorlint - errorlint
- copyloopvar - copyloopvar
- intrange
linters-settings: linters-settings:
depguard: depguard:

@ -40,10 +40,12 @@ func Test_generateRandomKey(t *testing.T) {
t.Run("Generated keys are unique", func(t *testing.T) { t.Run("Generated keys are unique", func(t *testing.T) {
keys := make(map[string]bool) keys := make(map[string]bool)
for i := 0; i < 100; i++ {
for range 100 {
key := GenerateRandomKey(8) key := GenerateRandomKey(8)
_, ok := keys[string(key)] _, ok := keys[string(key)]
is.False(ok) is.False(ok)
keys[string(key)] = true keys[string(key)] = true
} }
}) })

@ -93,6 +93,7 @@ type Func func(ctx context.Context) (any, error)
// Run runs a list of functions returns the results // Run runs a list of functions returns the results
func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, error) { func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, error) {
var wg sync.WaitGroup var wg sync.WaitGroup
resultsChan := make(chan Result, len(tasks)) resultsChan := make(chan Result, len(tasks))
taskChan := make(chan Func, 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() { runTask := func() {
defer wg.Done() defer wg.Done()
for fn := range taskChan { for fn := range taskChan {
result, err := fn(localCtx) result, err := fn(localCtx)
resultsChan <- Result{Result: result, Err: err} resultsChan <- Result{Result: result, Err: err}
@ -113,7 +115,7 @@ func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, erro
} }
// Start worker goroutines // Start worker goroutines
for i := 0; i < maxConcurrency; i++ { for range maxConcurrency {
wg.Add(1) wg.Add(1)
go runTask() go runTask()
} }
@ -135,8 +137,10 @@ func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, erro
for r := range resultsChan { for r := range resultsChan {
if r.Err != nil { if r.Err != nil {
cancelCtx() cancelCtx()
return nil, r.Err return nil, r.Err
} }
results = append(results, r) results = append(results, r)
} }

@ -33,7 +33,7 @@ func TestService_StackByWebhookID(t *testing.T) {
b := stackBuilder{t: t, store: store} b := stackBuilder{t: t, store: store}
b.createNewStack(newGuidString(t)) b.createNewStack(newGuidString(t))
for i := 0; i < 10; i++ { for range 10 {
b.createNewStack("") b.createNewStack("")
} }
webhookID := newGuidString(t) webhookID := newGuidString(t)

@ -1,6 +1,7 @@
package images package images
import ( import (
"cmp"
"strings" "strings"
"time" "time"
@ -41,8 +42,7 @@ func (c *RegistryClient) RegistryAuth(image Image) (string, string, error) {
} }
func (c *RegistryClient) CertainRegistryAuth(registry *portainer.Registry) (string, string, error) { func (c *RegistryClient) CertainRegistryAuth(registry *portainer.Registry) (string, string, error) {
err := registryutils.EnsureRegTokenValid(c.dataStore, registry) if err := registryutils.EnsureRegTokenValid(c.dataStore, registry); err != nil {
if err != nil {
return "", "", err return "", "", err
} }
@ -72,8 +72,7 @@ func (c *RegistryClient) EncodedRegistryAuth(image Image) (string, error) {
} }
func (c *RegistryClient) EncodedCertainRegistryAuth(registry *portainer.Registry) (string, error) { func (c *RegistryClient) EncodedCertainRegistryAuth(registry *portainer.Registry) (string, error) {
err := registryutils.EnsureRegTokenValid(c.dataStore, registry) if err := registryutils.EnsureRegTokenValid(c.dataStore, registry); err != nil {
if err != nil {
return "", err return "", err
} }
@ -92,38 +91,32 @@ func findBestMatchRegistry(repository string, registries []portainer.Registry) (
} }
var match1, match2, match3 *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:
// <USERNAME>/nginx:latest
// docker.io/<USERNAME>/nginx:latest
if strings.HasPrefix(repository, registry.Username+"/") || strings.HasPrefix(repository, registry.URL+"/"+registry.Username+"/") {
match1 = &registry
}
// try to match repository examples:
// portainer/portainer-ee:latest
// <NON-USERNAME>/portainer-ee:latest
if match3 == nil {
match3 = &registry
}
}
for _, registry := range registries {
if strings.Contains(repository, registry.URL) { if strings.Contains(repository, registry.URL) {
match2 = &registry match2 = &registry
} }
}
match := match1 if registry.Type != portainer.DockerHubRegistry {
if match == nil { continue
match = match2 }
}
if match == nil { // try to match repository examples:
match = match3 // <USERNAME>/nginx:latest
// docker.io/<USERNAME>/nginx:latest
if strings.HasPrefix(repository, registry.Username+"/") || strings.HasPrefix(repository, registry.URL+"/"+registry.Username+"/") {
match1 = &registry
}
// try to match repository examples:
// portainer/portainer-ee:latest
// <NON-USERNAME>/portainer-ee:latest
if match3 == nil {
match3 = &registry
}
} }
match := cmp.Or(match1, match2, match3)
if match == nil { if match == nil {
return nil, errors.New("no registries matched") return nil, errors.New("no registries matched")
} }

@ -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) { t.Run("can return the expected file content by multiple calls from one user", func(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(5) wg.Add(5)
for i := 0; i < 5; i++ {
for range 5 {
go func() { go func() {
singleAPIRequest(h, jwt1, is, "abcdefg") singleAPIRequest(h, jwt1, is, "abcdefg")
wg.Done() wg.Done()
}() }()
} }
wg.Wait() wg.Wait()
}) })
t.Run("can return the expected file content by multiple calls from different users", func(t *testing.T) { t.Run("can return the expected file content by multiple calls from different users", func(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(10) wg.Add(10)
for i := 0; i < 10; i++ {
for i := range 10 {
go func(j int) { go func(j int) {
if j%2 == 0 { if j%2 == 0 {
singleAPIRequest(h, jwt1, is, "abcdefg") singleAPIRequest(h, jwt1, is, "abcdefg")
} else { } else {
singleAPIRequest(h, jwt2, is, "abcdefg") singleAPIRequest(h, jwt2, is, "abcdefg")
} }
wg.Done() wg.Done()
}(i) }(i)
} }
wg.Wait() wg.Wait()
}) })

@ -27,7 +27,7 @@ func TestEndpointDeleteEdgeGroupsConcurrently(t *testing.T) {
var endpointIDs []portainer.EndpointID var endpointIDs []portainer.EndpointID
for i := 0; i < endpointsCount; i++ { for i := range endpointsCount {
endpointID := portainer.EndpointID(i) + 1 endpointID := portainer.EndpointID(i) + 1
if err := store.Endpoint().Create(&portainer.Endpoint{ if err := store.Endpoint().Create(&portainer.Endpoint{

@ -29,7 +29,7 @@ func TestTagDeleteEdgeGroupsConcurrently(t *testing.T) {
var tagIDs []portainer.TagID var tagIDs []portainer.TagID
for i := 0; i < tagsCount; i++ { for i := range tagsCount {
tagID := portainer.TagID(i) + 1 tagID := portainer.TagID(i) + 1
if err := store.Tag().Create(&portainer.Tag{ if err := store.Tag().Create(&portainer.Tag{

@ -27,7 +27,7 @@ func failFunc(t *testing.T) func() (string, error) {
func TestTokenCacheDataRace(t *testing.T) { func TestTokenCacheDataRace(t *testing.T) {
ch := make(chan struct{}) ch := make(chan struct{})
for i := 0; i < 1000; i++ { for range 1000 {
var tokenCache1, tokenCache2 *tokenCache var tokenCache1, tokenCache2 *tokenCache
mgr := NewTokenCacheManager() mgr := NewTokenCacheManager()

@ -13,7 +13,9 @@ import (
// IsLocalEndpoint returns true if this is a local environment(endpoint) // IsLocalEndpoint returns true if this is a local environment(endpoint)
func IsLocalEndpoint(endpoint *portainer.Endpoint) bool { 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) // 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) filteredEndpoints = append(filteredEndpoints, endpoint)
} }
} }
return filteredEndpoints return filteredEndpoints
} }
@ -187,12 +190,15 @@ func InitialStorageDetection(endpoint *portainer.Endpoint, endpointService datas
endpoint, endpoint,
) )
}() }()
log.Info().Msg("attempting to detect storage classes in the cluster") log.Info().Msg("attempting to detect storage classes in the cluster")
err := storageDetect(endpoint, endpointService, factory) err := storageDetect(endpoint, endpointService, factory)
if err == nil { if err == nil {
return return
} }
log.Err(err).Msg("error while detecting storage classes") log.Err(err).Msg("error while detecting storage classes")
go func() { go func() {
// Retry after 30 seconds if the initial detection failed. // Retry after 30 seconds if the initial detection failed.
log.Info().Msg("retrying storage detection in 30 seconds") 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) { func UpdateEdgeEndpointHeartbeat(endpoint *portainer.Endpoint, settings *portainer.Settings) {
if IsEdgeEndpoint(endpoint) { if !IsEdgeEndpoint(endpoint) {
endpoint.QueryDate = time.Now().Unix() return
checkInInterval := getEndpointCheckinInterval(endpoint, settings)
endpoint.Heartbeat = endpoint.QueryDate-endpoint.LastCheckInDate <= int64(checkInInterval*2+20)
} }
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 { func getEndpointCheckinInterval(endpoint *portainer.Endpoint, settings *portainer.Settings) int {
if endpoint.Edge.AsyncMode { if !endpoint.Edge.AsyncMode {
defaultInterval := 60 if endpoint.EdgeCheckinInterval > 0 {
intervals := [][]int{ return endpoint.EdgeCheckinInterval
{endpoint.Edge.PingInterval, settings.Edge.PingInterval},
{endpoint.Edge.CommandInterval, settings.Edge.CommandInterval},
{endpoint.Edge.SnapshotInterval, settings.Edge.SnapshotInterval},
} }
for i := 0; i < len(intervals); i++ { return settings.EdgeAgentCheckinInterval
effectiveInterval := intervals[i][0] }
if effectiveInterval <= 0 {
effectiveInterval = intervals[i][1]
}
if effectiveInterval > 0 && effectiveInterval < defaultInterval {
defaultInterval = effectiveInterval
}
}
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 { for i := range len(intervals) {
return endpoint.EdgeCheckinInterval effectiveInterval := intervals[i][0]
if effectiveInterval <= 0 {
effectiveInterval = intervals[i][1]
}
if effectiveInterval > 0 && effectiveInterval < defaultInterval {
defaultInterval = effectiveInterval
}
} }
return settings.EdgeAgentCheckinInterval return defaultInterval
} }

@ -16,6 +16,8 @@ import (
rbacv1types "k8s.io/client-go/kubernetes/typed/rbac/v1" 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 // 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) { func (kcl *KubeClient) IsRBACEnabled() (bool, error) {
namespace := "default" namespace := "default"
@ -23,11 +25,11 @@ func (kcl *KubeClient) IsRBACEnabled() (bool, error) {
resource := "resourcequotas" resource := "resourcequotas"
saClient := kcl.cli.CoreV1().ServiceAccounts(namespace) 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 saName := "portainer-rbac-test-sa-" + uniqueString
err := createServiceAccount(saClient, saName, namespace) if err := createServiceAccount(saClient, saName, namespace); err != nil {
if err != nil {
log.Error().Err(err).Msg("Error creating service account") log.Error().Err(err).Msg("Error creating service account")
return false, err return false, err
} }
defer deleteServiceAccount(saClient, saName) defer deleteServiceAccount(saClient, saName)
@ -36,29 +38,30 @@ func (kcl *KubeClient) IsRBACEnabled() (bool, error) {
allowed, err := checkServiceAccountAccess(accessReviewClient, saName, verb, resource, namespace) allowed, err := checkServiceAccountAccess(accessReviewClient, saName, verb, resource, namespace)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Error checking service account access") log.Error().Err(err).Msg("Error checking service account access")
return false, err 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 { if allowed {
return false, nil 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) roleClient := kcl.cli.RbacV1().Roles(namespace)
roleName := "portainer-rbac-test-role-" + uniqueString roleName := "portainer-rbac-test-role-" + uniqueString
err = createRole(roleClient, roleName, verb, resource, namespace) if err := createRole(roleClient, roleName, verb, resource, namespace); err != nil {
if err != nil {
log.Error().Err(err).Msg("Error creating role") log.Error().Err(err).Msg("Error creating role")
return false, err return false, err
} }
defer deleteRole(roleClient, roleName) defer deleteRole(roleClient, roleName)
roleBindingClient := kcl.cli.RbacV1().RoleBindings(namespace) roleBindingClient := kcl.cli.RbacV1().RoleBindings(namespace)
roleBindingName := "portainer-rbac-test-role-binding-" + uniqueString roleBindingName := "portainer-rbac-test-role-binding-" + uniqueString
err = createRoleBinding(roleBindingClient, roleBindingName, roleName, saName, namespace) if err := createRoleBinding(roleBindingClient, roleBindingName, roleName, saName, namespace); err != nil {
if err != nil {
log.Error().Err(err).Msg("Error creating role binding") log.Error().Err(err).Msg("Error creating role binding")
return false, err return false, err
} }
defer deleteRoleBinding(roleBindingClient, roleBindingName) defer deleteRoleBinding(roleBindingClient, roleBindingName)
@ -66,10 +69,11 @@ func (kcl *KubeClient) IsRBACEnabled() (bool, error) {
allowed, err = checkServiceAccountAccess(accessReviewClient, saName, verb, resource, namespace) allowed, err = checkServiceAccountAccess(accessReviewClient, saName, verb, resource, namespace)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Error checking service account access with authorizations added") log.Error().Err(err).Msg("Error checking service account access with authorizations added")
return false, err 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 return allowed, nil
} }
@ -80,13 +84,13 @@ func createServiceAccount(saClient corev1types.ServiceAccountInterface, name str
Namespace: namespace, Namespace: namespace,
}, },
} }
_, err := saClient.Create(context.Background(), serviceAccount, metav1.CreateOptions{}) _, err := saClient.Create(context.Background(), serviceAccount, metav1.CreateOptions{})
return err return err
} }
func deleteServiceAccount(saClient corev1types.ServiceAccountInterface, name string) { func deleteServiceAccount(saClient corev1types.ServiceAccountInterface, name string) {
err := saClient.Delete(context.Background(), name, metav1.DeleteOptions{}) if err := saClient.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil {
if err != nil {
log.Error().Err(err).Msg("Error deleting service account: " + name) 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{}) _, err := roleClient.Create(context.Background(), role, metav1.CreateOptions{})
return err return err
} }
func deleteRole(roleClient rbacv1types.RoleInterface, name string) { func deleteRole(roleClient rbacv1types.RoleInterface, name string) {
err := roleClient.Delete(context.Background(), name, metav1.DeleteOptions{}) if err := roleClient.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil {
if err != nil {
log.Error().Err(err).Msg("Error deleting role: " + name) log.Error().Err(err).Msg("Error deleting role: " + name)
} }
} }
@ -134,17 +138,19 @@ func createRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, clust
APIGroup: "rbac.authorization.k8s.io", APIGroup: "rbac.authorization.k8s.io",
}, },
} }
roleBinding, err := roleBindingClient.Create(context.Background(), clusterRoleBinding, metav1.CreateOptions{}) roleBinding, err := roleBindingClient.Create(context.Background(), clusterRoleBinding, metav1.CreateOptions{})
if err != nil { if err != nil {
log.Error().Err(err).Msg("Error creating role binding: " + clusterRoleBindingName) log.Error().Err(err).Msg("Error creating role binding: " + clusterRoleBindingName)
return err return err
} }
// Retry checkRoleBinding a maximum of 5 times with a 100ms wait after each attempt // Retry checkRoleBinding a maximum of 5 times with a 100ms wait after each attempt
maxRetries := 5 for range maxRetries {
for i := 0; i < maxRetries; i++ {
err = checkRoleBinding(roleBindingClient, roleBinding.Name) err = checkRoleBinding(roleBindingClient, roleBinding.Name)
time.Sleep(100 * time.Millisecond) // Wait for 100ms, even if the check passes time.Sleep(100 * time.Millisecond) // Wait for 100ms, even if the check passes
if err == nil { if err == nil {
break break
} }
@ -154,17 +160,17 @@ func createRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, clust
} }
func checkRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, name string) error { func checkRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, name string) error {
_, err := roleBindingClient.Get(context.Background(), name, metav1.GetOptions{}) if _, err := roleBindingClient.Get(context.Background(), name, metav1.GetOptions{}); err != nil {
if err != nil {
log.Error().Err(err).Msg("Error finding rolebinding: " + name) log.Error().Err(err).Msg("Error finding rolebinding: " + name)
return err return err
} }
return nil return nil
} }
func deleteRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, name string) { func deleteRoleBinding(roleBindingClient rbacv1types.RoleBindingInterface, name string) {
err := roleBindingClient.Delete(context.Background(), name, metav1.DeleteOptions{}) if err := roleBindingClient.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil {
if err != nil {
log.Error().Err(err).Msg("Error deleting role binding: " + name) 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 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{}) result, err := accessReviewClient.Create(context.Background(), subjectAccessReview, metav1.CreateOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
return result.Status.Allowed, nil return result.Status.Allowed, nil
} }

@ -50,13 +50,12 @@ var funcmap = map[StackRemoteOperation]buildCmdFunc{
// build the unpacker cmd for stack based on stackOperation // build the unpacker cmd for stack based on stackOperation
func (d *stackDeployer) buildUnpackerCmdForStack(stack *portainer.Stack, operation StackRemoteOperation, opts unpackerCmdBuilderOptions) ([]string, error) { func (d *stackDeployer) buildUnpackerCmdForStack(stack *portainer.Stack, operation StackRemoteOperation, opts unpackerCmdBuilderOptions) ([]string, error) {
fn := funcmap[operation] fn := funcmap[operation]
if fn == nil { if fn == nil {
return nil, fmt.Errorf("unknown stack operation %s", operation) 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) envStrings := getEnv(stack.Env)
return fn(stack, opts, registriesStrings, envStrings), nil 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] <git-repo-url> <ref> <project-name> <destination> <compose-file-path> [<more-file-paths>...] // deploy [-u username -p password] [--skip-tls-verify] [--env KEY1=VALUE1 --env KEY2=VALUE2] <git-repo-url> <ref> <project-name> <destination> <compose-file-path> [<more-file-paths>...]
func buildDeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { func buildDeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string {
cmd := []string{} cmd := []string{UnpackerCmdDeploy}
cmd = append(cmd, UnpackerCmdDeploy)
cmd = appendGitAuthIfNeeded(cmd, stack) cmd = appendGitAuthIfNeeded(cmd, stack)
cmd = appendSkipTLSVerifyIfNeeded(cmd, stack) cmd = appendSkipTLSVerifyIfNeeded(cmd, stack)
cmd = append(cmd, env...) cmd = append(cmd, env...)
cmd = append(cmd, registries...) cmd = append(cmd, registries...)
cmd = append(cmd, stack.GitConfig.URL) cmd = append(cmd,
cmd = append(cmd, stack.GitConfig.ReferenceName) stack.GitConfig.URL,
cmd = append(cmd, stack.Name) stack.GitConfig.ReferenceName,
cmd = append(cmd, opts.composeDestination) stack.Name,
cmd = append(cmd, stack.EntryPoint) opts.composeDestination,
cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) stack.EntryPoint,
return cmd )
return append(cmd, stack.AdditionalFiles...)
} }
// undeploy [-u username -p password] [-k] <git-repo-url> <project-name> <destination> <compose-file-path> [<more-file-paths>...] // undeploy [-u username -p password] [-k] <git-repo-url> <project-name> <destination> <compose-file-path> [<more-file-paths>...]
func buildUndeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { func buildUndeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string {
cmd := []string{} cmd := []string{UnpackerCmdUndeploy}
cmd = append(cmd, UnpackerCmdUndeploy)
cmd = appendGitAuthIfNeeded(cmd, stack) cmd = appendGitAuthIfNeeded(cmd, stack)
cmd = append(cmd, stack.GitConfig.URL) cmd = append(cmd, stack.GitConfig.URL,
cmd = append(cmd, stack.Name) stack.Name,
cmd = append(cmd, opts.composeDestination) opts.composeDestination,
cmd = append(cmd, stack.EntryPoint) stack.EntryPoint,
cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) )
return cmd
return append(cmd, stack.AdditionalFiles...)
} }
// deploy [-u username -p password] [--skip-tls-verify] [-k] [--env KEY1=VALUE1 --env KEY2=VALUE2] <git-repo-url> <project-name> <destination> <compose-file-path> [<more-file-paths>...] // deploy [-u username -p password] [--skip-tls-verify] [-k] [--env KEY1=VALUE1 --env KEY2=VALUE2] <git-repo-url> <project-name> <destination> <compose-file-path> [<more-file-paths>...]
func buildComposeStartCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { func buildComposeStartCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string {
cmd := []string{} cmd := []string{UnpackerCmdDeploy}
cmd = append(cmd, UnpackerCmdDeploy)
cmd = appendGitAuthIfNeeded(cmd, stack) cmd = appendGitAuthIfNeeded(cmd, stack)
cmd = appendSkipTLSVerifyIfNeeded(cmd, stack) cmd = appendSkipTLSVerifyIfNeeded(cmd, stack)
cmd = append(cmd, "-k") cmd = append(cmd, "-k")
cmd = append(cmd, env...) cmd = append(cmd, env...)
cmd = append(cmd, registries...) cmd = append(cmd, registries...)
cmd = append(cmd, stack.GitConfig.URL) cmd = append(cmd, stack.GitConfig.URL,
cmd = append(cmd, stack.GitConfig.ReferenceName) stack.GitConfig.ReferenceName,
cmd = append(cmd, stack.Name) stack.Name,
cmd = append(cmd, opts.composeDestination) opts.composeDestination,
cmd = append(cmd, stack.EntryPoint) stack.EntryPoint,
cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) )
return cmd
return append(cmd, stack.AdditionalFiles...)
} }
// undeploy [-u username -p password] [-k] <git-repo-url> <project-name> <destination> <compose-file-path> [<more-file-paths>...] // undeploy [-u username -p password] [-k] <git-repo-url> <project-name> <destination> <compose-file-path> [<more-file-paths>...]
func buildComposeStopCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { func buildComposeStopCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string {
cmd := []string{} cmd := []string{UnpackerCmdUndeploy}
cmd = append(cmd, UnpackerCmdUndeploy)
cmd = appendGitAuthIfNeeded(cmd, stack) cmd = appendGitAuthIfNeeded(cmd, stack)
cmd = append(cmd, "-k") cmd = append(cmd,
cmd = append(cmd, stack.GitConfig.URL) "-k",
cmd = append(cmd, stack.Name) stack.GitConfig.URL,
cmd = append(cmd, opts.composeDestination) stack.Name,
cmd = append(cmd, stack.EntryPoint) opts.composeDestination,
cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) stack.EntryPoint,
return cmd )
return append(cmd, stack.AdditionalFiles...)
} }
// swarm-deploy [-u username -p password] [--skip-tls-verify] [-f] [-r] [--env KEY1=VALUE1 --env KEY2=VALUE2] <git-repo-url> <git-ref> <project-name> <destination> <compose-file-path> [<more-file-paths>...] // swarm-deploy [-u username -p password] [--skip-tls-verify] [-f] [-r] [--env KEY1=VALUE1 --env KEY2=VALUE2] <git-repo-url> <git-ref> <project-name> <destination> <compose-file-path> [<more-file-paths>...]
func buildSwarmDeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { func buildSwarmDeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string {
cmd := []string{} cmd := []string{UnpackerCmdSwarmDeploy}
cmd = append(cmd, UnpackerCmdSwarmDeploy)
cmd = appendGitAuthIfNeeded(cmd, stack) cmd = appendGitAuthIfNeeded(cmd, stack)
cmd = appendSkipTLSVerifyIfNeeded(cmd, stack) cmd = appendSkipTLSVerifyIfNeeded(cmd, stack)
if opts.pullImage { if opts.pullImage {
cmd = append(cmd, "-f") cmd = append(cmd, "-f")
} }
@ -137,85 +138,76 @@ func buildSwarmDeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions,
if opts.prune { if opts.prune {
cmd = append(cmd, "-r") cmd = append(cmd, "-r")
} }
cmd = append(cmd, env...) cmd = append(cmd, env...)
cmd = append(cmd, registries...) cmd = append(cmd, registries...)
cmd = append(cmd, stack.GitConfig.URL) cmd = append(cmd, stack.GitConfig.URL,
cmd = append(cmd, stack.GitConfig.ReferenceName) stack.GitConfig.ReferenceName,
cmd = append(cmd, stack.Name) stack.Name,
cmd = append(cmd, opts.composeDestination) opts.composeDestination,
cmd = append(cmd, stack.EntryPoint) stack.EntryPoint,
cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) )
return cmd
return append(cmd, stack.AdditionalFiles...)
} }
// swarm-undeploy [-k] <project-name> <destination> // swarm-undeploy [-k] <project-name> <destination>
func buildSwarmUndeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { func buildSwarmUndeployCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string {
cmd := []string{} return []string{UnpackerCmdSwarmUndeploy, stack.Name, opts.composeDestination}
cmd = append(cmd, UnpackerCmdSwarmUndeploy)
cmd = append(cmd, stack.Name)
cmd = append(cmd, opts.composeDestination)
return cmd
} }
// swarm-deploy [-u username -p password] [-f] [-r] [-k] [--skip-tls-verify] [--env KEY1=VALUE1 --env KEY2=VALUE2] <git-repo-url> <project-name> <destination> <compose-file-path> [<more-file-paths>...] // swarm-deploy [-u username -p password] [-f] [-r] [-k] [--skip-tls-verify] [--env KEY1=VALUE1 --env KEY2=VALUE2] <git-repo-url> <project-name> <destination> <compose-file-path> [<more-file-paths>...]
func buildSwarmStartCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { func buildSwarmStartCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string {
cmd := []string{} cmd := []string{UnpackerCmdSwarmDeploy, "-f", "-r", "-k"}
cmd = append(cmd, UnpackerCmdSwarmDeploy, "-f", "-r", "-k")
cmd = appendSkipTLSVerifyIfNeeded(cmd, stack) cmd = appendSkipTLSVerifyIfNeeded(cmd, stack)
cmd = append(cmd, getEnv(stack.Env)...) cmd = append(cmd, getEnv(stack.Env)...)
cmd = append(cmd, registries...) cmd = append(cmd, registries...)
cmd = append(cmd, stack.GitConfig.URL) cmd = append(cmd, stack.GitConfig.URL,
cmd = append(cmd, stack.GitConfig.ReferenceName) stack.GitConfig.ReferenceName,
cmd = append(cmd, stack.Name) stack.Name,
cmd = append(cmd, opts.composeDestination) opts.composeDestination,
cmd = append(cmd, stack.EntryPoint) stack.EntryPoint,
cmd = appendAdditionalFiles(cmd, stack.AdditionalFiles) )
return cmd
return append(cmd, stack.AdditionalFiles...)
} }
// swarm-undeploy [-k] <project-name> <destination> // swarm-undeploy [-k] <project-name> <destination>
func buildSwarmStopCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string { func buildSwarmStopCmd(stack *portainer.Stack, opts unpackerCmdBuilderOptions, registries []string, env []string) []string {
cmd := []string{} return []string{UnpackerCmdSwarmUndeploy, "-k", stack.Name, opts.composeDestination}
cmd = append(cmd, UnpackerCmdSwarmUndeploy, "-k")
cmd = append(cmd, stack.Name)
cmd = append(cmd, opts.composeDestination)
return cmd
} }
func appendGitAuthIfNeeded(cmd []string, stack *portainer.Stack) []string { func appendGitAuthIfNeeded(cmd []string, stack *portainer.Stack) []string {
if stack.GitConfig.Authentication != nil && len(stack.GitConfig.Authentication.Password) != 0 { if stack.GitConfig.Authentication == nil || stack.GitConfig.Authentication.Password == "" {
cmd = append(cmd, "-u", stack.GitConfig.Authentication.Username, "-p", 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 { func appendSkipTLSVerifyIfNeeded(cmd []string, stack *portainer.Stack) []string {
if stack.GitConfig.TLSSkipVerify { if !stack.GitConfig.TLSSkipVerify {
cmd = append(cmd, "--skip-tls-verify") return cmd
} }
return cmd
}
func appendAdditionalFiles(cmd []string, files []string) []string { return append(cmd, "--skip-tls-verify")
for i := 0; i < len(files); i++ {
cmd = append(cmd, files[i])
}
return cmd
} }
func getRegistry(registries []portainer.Registry, dataStore dataservices.DataStore) []string { func generateRegistriesStrings(registries []portainer.Registry, dataStore dataservices.DataStore) []string {
cmds := []string{} cmds := []string{}
for _, registry := range registries { for _, registry := range registries {
if registry.Authentication { if registry.Authentication {
err := registryutils.EnsureRegTokenValid(dataStore, &registry) if err := registryutils.EnsureRegTokenValid(dataStore, &registry); err != nil {
if err == nil { continue
username, password, err := registryutils.GetRegEffectiveCredential(&registry) }
if err == nil {
cmd := fmt.Sprintf("--registry=%s:%s:%s", username, password, registry.URL) username, password, err := registryutils.GetRegEffectiveCredential(&registry)
cmds = append(cmds, cmd) if err != nil {
} continue
} }
cmds = append(cmds, fmt.Sprintf("--registry=%s:%s:%s", username, password, registry.URL))
} }
} }

Loading…
Cancel
Save