Skip to content

Commit

Permalink
Merge pull request #3 from plakyda-codefresh/feat-retry-app-creation
Browse files Browse the repository at this point in the history
Feat retry app creation
  • Loading branch information
pasha-codefresh authored Sep 18, 2021
2 parents 6f794d0 + cc47361 commit 0710be4
Show file tree
Hide file tree
Showing 17 changed files with 268 additions and 110 deletions.
23 changes: 11 additions & 12 deletions cmd/argocd/commands/headless/headless.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import (
"github.com/golang/protobuf/ptypes/empty"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"

argoapi "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
Expand Down Expand Up @@ -43,21 +43,20 @@ func testAPI(clientOpts *argoapi.ClientOptions) error {
return err
}

func addKubectlFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
overrides := clientcmd.ConfigOverrides{}
kflags := clientcmd.RecommendedConfigOverrideFlags("")
cmd.Flags().StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
clientcmd.BindOverrideFlags(&overrides, cmd.Flags(), kflags)
return clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)
}

// InitCommand allows executing command in a headless mode: on the fly starts Argo CD API server and
// changes provided client options to use started API server port
func InitCommand(cmd *cobra.Command, clientOpts *argoapi.ClientOptions, port *int) *cobra.Command {
ctx, cancel := context.WithCancel(context.Background())
clientConfig := addKubectlFlagsToCmd(cmd)
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
clientConfig := cli.AddKubectlFlagsToSet(flags)
// copy k8s persistent flags into argocd command flags
flags.VisitAll(func(flag *pflag.Flag) {
// skip Kubernetes server flags since argocd has it's own server flag
if flag.Name == "server" {
return
}
cmd.Flags().AddFlag(flag)
})
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
startInProcessAPI := clientOpts.Core
if !startInProcessAPI {
Expand Down
2 changes: 1 addition & 1 deletion docs/proposals/002-ui-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ It is proposed that a git repository be used to contain the javascript code, as

In the most simplest form, an Argo CD extension could simply be a pointer to a git repository at a revision:

```yaml=
```yaml
kind: ArgoCDExtension
metadata:
name: argo-rollouts
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d // indirect
github.com/prometheus/client_golang v1.7.1
github.com/r3labs/diff v1.1.0
github.com/robfig/cron v1.1.0
github.com/rs/cors v1.6.0 // indirect
github.com/sirupsen/logrus v1.7.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,8 @@ github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULU
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quobyte/api v0.1.8/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI=
github.com/r3labs/diff v1.1.0 h1:V53xhrbTHrWFWq3gI4b94AjgEJOerO1+1l0xyHOBi8M=
github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6Xig=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY=
github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
Expand Down
218 changes: 130 additions & 88 deletions reposerver/apiclient/repository.pb.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion reposerver/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -1179,7 +1179,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
return nil
}

settings := operationSettings{allowConcurrent: q.Source.AllowsConcurrentProcessing(), noCache: q.NoCache, noRevisionCache: q.NoCache}
settings := operationSettings{allowConcurrent: q.Source.AllowsConcurrentProcessing(), noCache: q.NoCache, noRevisionCache: q.NoCache || q.NoRevisionCache}
err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, false, cacheFn, operation, settings)

return res, err
Expand Down
1 change: 1 addition & 0 deletions reposerver/repository/repository.proto
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ message RepoServerAppDetailsQuery {
github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizeOptions kustomizeOptions = 4;
string appName = 5;
bool noCache = 6;
bool noRevisionCache = 7;
}

// RepoAppDetailsResponse application details
Expand Down
2 changes: 1 addition & 1 deletion server/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
return existing, nil
}
if q.Upsert == nil || !*q.Upsert {
return nil, status.Errorf(codes.InvalidArgument, "existing application spec is different, use upsert flag to force update")
return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("application", existing.Spec, a.Spec))
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, appRBACName(a)); err != nil {
return nil, err
Expand Down
4 changes: 3 additions & 1 deletion server/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cluster
import (
"time"

"github.com/argoproj/argo-cd/v2/util/argo"

"github.com/argoproj/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
Expand Down Expand Up @@ -93,7 +95,7 @@ func (s *Server) Create(ctx context.Context, q *cluster.ClusterCreateRequest) (*
} else if q.Upsert {
return s.Update(ctx, &cluster.ClusterUpdateRequest{Cluster: c})
} else {
return nil, status.Errorf(codes.InvalidArgument, "existing cluster spec is different; use upsert flag to force update")
return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("cluster", existing, c))
}
}
err = s.cache.SetClusterInfo(c.Server, &appv1.ClusterInfo{
Expand Down
2 changes: 1 addition & 1 deletion server/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func (s *Server) Create(ctx context.Context, q *project.ProjectCreateRequest) (*
res, err = s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Update(ctx, existing, metav1.UpdateOptions{})
} else {
if !reflect.DeepEqual(existing.Spec, q.GetProject().Spec) {
return nil, status.Errorf(codes.InvalidArgument, "existing project spec is different, use upsert flag to force update")
return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("project", existing.Spec, q.GetProject().Spec))
}
return existing, nil
}
Expand Down
4 changes: 3 additions & 1 deletion server/repocreds/repocreds.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package repocreds
import (
"reflect"

"github.com/argoproj/argo-cd/v2/util/argo"

"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -91,7 +93,7 @@ func (s *Server) CreateRepositoryCredentials(ctx context.Context, q *repocredspk
} else if q.Upsert {
return s.UpdateRepositoryCredentials(ctx, &repocredspkg.RepoCredsUpdateRequest{Creds: r})
} else {
return nil, status.Errorf(codes.InvalidArgument, "existing repository credentials spec is different; use upsert flag to force update")
return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("repository credentials", existing, r))
}
}
return &appsv1.RepoCreds{URL: r.URL}, err
Expand Down
4 changes: 3 additions & 1 deletion server/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"reflect"

"github.com/argoproj/argo-cd/v2/util/argo"

"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/utils/text"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -343,7 +345,7 @@ func (s *Server) CreateRepository(ctx context.Context, q *repositorypkg.RepoCrea
r.Project = q.Repo.Project
return s.UpdateRepository(ctx, &repositorypkg.RepoUpdateRequest{Repo: r})
} else {
return nil, status.Errorf(codes.InvalidArgument, "existing repository spec is different; use upsert flag to force update")
return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("repository", existing, r))
}
}
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {RevisionHelpIcon, YamlEditor} from '../../../shared/components';
import * as models from '../../../shared/models';
import {services} from '../../../shared/services';
import {ApplicationParameters} from '../application-parameters/application-parameters';
import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options';
import {ApplicationSyncOptionsField} from '../application-sync-options/application-sync-options';
import {RevisionFormField} from '../revision-form-field/revision-form-field';

Expand Down Expand Up @@ -221,6 +222,7 @@ export const ApplicationCreatePanel = (props: {
<div className='argo-form-row'>
<label>Sync Options</label>
<FormField formApi={api} field='spec.syncPolicy.syncOptions' component={ApplicationSyncOptionsField} />
<ApplicationRetryOptions formApi={api} />
</div>
</div>
);
Expand Down
25 changes: 25 additions & 0 deletions util/argo/argo.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"strings"
"time"

"github.com/r3labs/diff"

"github.com/argoproj/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -240,6 +242,8 @@ func ValidateRepo(
Source: &spec.Source,
Repos: permittedHelmRepos,
KustomizeOptions: kustomizeOptions,
// don't use case during application change to make sure to fetch latest git/helm revisions
NoRevisionCache: true,
})
if err != nil {
conditions = append(conditions, argoappv1.ApplicationCondition{
Expand Down Expand Up @@ -692,3 +696,24 @@ func mergeVirtualProject(proj *argoappv1.AppProject, globalProj *argoappv1.AppPr

return proj
}

func GenerateSpecIsDifferentErrorMessage(entity string, a, b interface{}) string {
basicMsg := fmt.Sprintf("existing %s spec is different; use upsert flag to force update", entity)
difference, _ := GetDifferentPathsBetweenStructs(a, b)
if len(difference) == 0 {
return basicMsg
}
return fmt.Sprintf("%s; difference in keys \"%s\"", basicMsg, strings.Join(difference[:], ","))
}

func GetDifferentPathsBetweenStructs(a, b interface{}) ([]string, error) {
var difference []string
changelog, err := diff.Diff(a, b)
if err != nil {
return nil, err
}
for _, changeItem := range changelog {
difference = append(difference, changeItem.Path...)
}
return difference, nil
}
35 changes: 35 additions & 0 deletions util/argo/argo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ func TestValidateRepo(t *testing.T) {
Source: &app.Spec.Source,
Repos: helmRepos,
KustomizeOptions: kustomizeOptions,
NoRevisionCache: true,
}).Return(&apiclient.RepoAppDetailsResponse{}, nil)

repo.Type = "git"
Expand Down Expand Up @@ -880,3 +881,37 @@ func Test_retrieveScopedRepositoriesWithNotProjectAssigned(t *testing.T) {
assert.Len(t, scopedRepos, 0)

}

func Test_GetDifferentPathsBetweenStructs(t *testing.T) {

r1 := argoappv1.Repository{}
r2 := argoappv1.Repository{
Name: "SomeName",
}

difference, _ := GetDifferentPathsBetweenStructs(r1, r2)
assert.Equal(t, difference, []string{"Name"})

}

func Test_GenerateSpecIsDifferentErrorMessageWithNoDiff(t *testing.T) {

r1 := argoappv1.Repository{}
r2 := argoappv1.Repository{}

msg := GenerateSpecIsDifferentErrorMessage("application", r1, r2)
assert.Equal(t, msg, "existing application spec is different; use upsert flag to force update")

}

func Test_GenerateSpecIsDifferentErrorMessageWithDiff(t *testing.T) {

r1 := argoappv1.Repository{}
r2 := argoappv1.Repository{
Name: "test",
}

msg := GenerateSpecIsDifferentErrorMessage("repo", r1, r2)
assert.Equal(t, msg, "existing repo spec is different; use upsert flag to force update; difference in keys \"Name\"")

}
38 changes: 38 additions & 0 deletions util/cache/redis_hook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cache

import (
"context"
"errors"
"testing"

"github.com/stretchr/testify/assert"

"github.com/go-redis/redis/v8"
)

func Test_ReconnectCallbackHookCalled(t *testing.T) {
called := false
hook := NewArgoRedisHook(func() {
called = true
})

cmd := &redis.StringCmd{}
cmd.SetErr(errors.New("Failed to resync revoked tokens. retrying again in 1 minute: dial tcp: lookup argocd-redis on 10.179.0.10:53: no such host"))

_ = hook.AfterProcess(context.Background(), cmd)

assert.Equal(t, called, true)
}

func Test_ReconnectCallbackHookNotCalled(t *testing.T) {
called := false
hook := NewArgoRedisHook(func() {
called = true
})
cmd := &redis.StringCmd{}
cmd.SetErr(errors.New("Something wrong"))

_ = hook.AfterProcess(context.Background(), cmd)

assert.Equal(t, called, false)
}
13 changes: 10 additions & 3 deletions util/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/google/shlex"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
terminal "golang.org/x/term"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -58,15 +59,21 @@ func NewVersionCmd(cliName string) *cobra.Command {
return &versionCmd
}

// AddKubectlFlagsToCmd adds kubectl like flags to a command and returns the ClientConfig interface
// AddKubectlFlagsToCmd adds kubectl like flags to a persistent flags of a command and returns the ClientConfig interface
// for retrieving the values.
func AddKubectlFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig {
return AddKubectlFlagsToSet(cmd.PersistentFlags())
}

// AddKubectlFlagsToSet adds kubectl like flags to a provided flag set and returns the ClientConfig interface
// for retrieving the values.
func AddKubectlFlagsToSet(flags *pflag.FlagSet) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
overrides := clientcmd.ConfigOverrides{}
kflags := clientcmd.RecommendedConfigOverrideFlags("")
cmd.PersistentFlags().StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
clientcmd.BindOverrideFlags(&overrides, cmd.PersistentFlags(), kflags)
flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
clientcmd.BindOverrideFlags(&overrides, flags, kflags)
return clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)
}

Expand Down

0 comments on commit 0710be4

Please sign in to comment.