mirror of https://github.com/portainer/portainer
chore(code): use int ranges in loops BE-10990 (#12028)
parent
468c12c75b
commit
31bdb948a8
|
@ -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 = ®istry
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to match repository examples:
|
|
||||||
// portainer/portainer-ee:latest
|
|
||||||
// <NON-USERNAME>/portainer-ee:latest
|
|
||||||
if match3 == nil {
|
|
||||||
match3 = ®istry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for _, registry := range registries {
|
||||||
if strings.Contains(repository, registry.URL) {
|
if strings.Contains(repository, registry.URL) {
|
||||||
match2 = ®istry
|
match2 = ®istry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if registry.Type != portainer.DockerHubRegistry {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = ®istry
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to match repository examples:
|
||||||
|
// portainer/portainer-ee:latest
|
||||||
|
// <NON-USERNAME>/portainer-ee:latest
|
||||||
|
if match3 == nil {
|
||||||
|
match3 = ®istry
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match := match1
|
match := cmp.Or(match1, match2, match3)
|
||||||
if match == nil {
|
|
||||||
match = match2
|
|
||||||
}
|
|
||||||
if match == nil {
|
|
||||||
match = 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]
|
defaultInterval := 60
|
||||||
}
|
intervals := [][]int{
|
||||||
if effectiveInterval > 0 && effectiveInterval < defaultInterval {
|
{endpoint.Edge.PingInterval, settings.Edge.PingInterval},
|
||||||
defaultInterval = effectiveInterval
|
{endpoint.Edge.CommandInterval, settings.Edge.CommandInterval},
|
||||||
}
|
{endpoint.Edge.SnapshotInterval, settings.Edge.SnapshotInterval},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range len(intervals) {
|
||||||
|
effectiveInterval := intervals[i][0]
|
||||||
|
if effectiveInterval <= 0 {
|
||||||
|
effectiveInterval = intervals[i][1]
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultInterval
|
if effectiveInterval > 0 && effectiveInterval < defaultInterval {
|
||||||
|
defaultInterval = effectiveInterval
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if endpoint.EdgeCheckinInterval > 0 {
|
return defaultInterval
|
||||||
return endpoint.EdgeCheckinInterval
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings.EdgeAgentCheckinInterval
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
|
return append(cmd, "--skip-tls-verify")
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendAdditionalFiles(cmd []string, files []string) []string {
|
func generateRegistriesStrings(registries []portainer.Registry, dataStore dataservices.DataStore) []string {
|
||||||
for i := 0; i < len(files); i++ {
|
|
||||||
cmd = append(cmd, files[i])
|
|
||||||
}
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRegistry(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, ®istry)
|
if err := registryutils.EnsureRegTokenValid(dataStore, ®istry); err != nil {
|
||||||
if err == nil {
|
continue
|
||||||
username, password, err := registryutils.GetRegEffectiveCredential(®istry)
|
|
||||||
if err == nil {
|
|
||||||
cmd := fmt.Sprintf("--registry=%s:%s:%s", username, password, registry.URL)
|
|
||||||
cmds = append(cmds, cmd)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
username, password, err := registryutils.GetRegEffectiveCredential(®istry)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds = append(cmds, fmt.Sprintf("--registry=%s:%s:%s", username, password, registry.URL))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue