Skip to content

Commit

Permalink
added timeout flag to app create - wait until App is Synced (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
ATGardner authored Jun 28, 2021
1 parent 3c8be61 commit 6ea3c29
Show file tree
Hide file tree
Showing 12 changed files with 306 additions and 126 deletions.
93 changes: 71 additions & 22 deletions cmd/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (
"fmt"
"os"
"text/tabwriter"
"time"

"github.com/argoproj-labs/argocd-autopilot/pkg/application"
"github.com/argoproj-labs/argocd-autopilot/pkg/argocd"
"github.com/argoproj-labs/argocd-autopilot/pkg/fs"
"github.com/argoproj-labs/argocd-autopilot/pkg/git"
"github.com/argoproj-labs/argocd-autopilot/pkg/kube"
"github.com/argoproj-labs/argocd-autopilot/pkg/log"
"github.com/argoproj-labs/argocd-autopilot/pkg/store"
"github.com/argoproj-labs/argocd-autopilot/pkg/util"
Expand All @@ -28,6 +31,8 @@ type (
AppsCloneOpts *git.CloneOptions
ProjectName string
AppOpts *application.CreateOptions
KubeFactory kube.Factory
Timeout time.Duration
}

AppDeleteOptions struct {
Expand Down Expand Up @@ -57,7 +62,6 @@ func NewAppCommand() *cobra.Command {
}
cloneOpts = git.AddFlags(cmd, &git.AddFlagsOptions{
FS: memfs.New(),
Required: true,
})

cmd.AddCommand(NewAppCreateCommand(cloneOpts))
Expand All @@ -72,6 +76,8 @@ func NewAppCreateCommand(cloneOpts *git.CloneOptions) *cobra.Command {
appsCloneOpts *git.CloneOptions
appOpts *application.CreateOptions
projectName string
timeout time.Duration
f kube.Factory
)

cmd := &cobra.Command{
Expand Down Expand Up @@ -106,32 +112,42 @@ func NewAppCreateCommand(cloneOpts *git.CloneOptions) *cobra.Command {
# Reference a specific git branch:
<BIN> app create <new_app_name> --app github.com/some_org/some_repo/manifests?ref=<branch_name> --project project_name
# Wait until the application is Synced in the cluster:
<BIN> app create <new_app_name> --app github.com/some_org/some_repo/manifests --project project_name --wait-timeout 2m --context my_context
`),
PreRun: func(_ *cobra.Command, _ []string) {
cloneOpts.Parse()
appsCloneOpts.Parse()
},
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if len(args) < 1 {
log.G().Fatal("must enter application name")
log.G(ctx).Fatal("must enter application name")
}

appOpts.AppName = args[0]
return RunAppCreate(cmd.Context(), &AppCreateOptions{
return RunAppCreate(ctx, &AppCreateOptions{
CloneOpts: cloneOpts,
AppsCloneOpts: appsCloneOpts,
ProjectName: projectName,
AppOpts: appOpts,
Timeout: timeout,
KubeFactory: f,
})
},
}

cmd.Flags().StringVarP(&projectName, "project", "p", "", "Project name")
cmd.Flags().DurationVar(&timeout, "wait-timeout", time.Duration(0), "If not '0s', will try to connect to the cluster and wait until the application is in 'Synced' status for the specified timeout period")
appsCloneOpts = git.AddFlags(cmd, &git.AddFlagsOptions{
FS: memfs.New(),
Prefix: "apps",
FS: memfs.New(),
Prefix: "apps",
Optional: true,
})
appOpts = application.AddFlags(cmd)
f = kube.AddFlags(cmd.Flags())

die(cmd.MarkFlagRequired("app"))
die(cmd.MarkFlagRequired("project"))
Expand All @@ -140,16 +156,16 @@ func NewAppCreateCommand(cloneOpts *git.CloneOptions) *cobra.Command {
}

func RunAppCreate(ctx context.Context, opts *AppCreateOptions) error {
r, repofs, err := prepareRepo(ctx, opts.CloneOpts, opts.ProjectName)
if err != nil {
return err
}

var (
appsRepo git.Repository
appsfs fs.FS
)

r, repofs, err := prepareRepo(ctx, opts.CloneOpts, opts.ProjectName)
if err != nil {
return err
}

if opts.AppsCloneOpts.Repo != "" {
if opts.AppsCloneOpts.Auth.Password == "" {
opts.AppsCloneOpts.Auth.Password = opts.CloneOpts.Auth.Password
Expand Down Expand Up @@ -182,19 +198,36 @@ func RunAppCreate(ctx context.Context, opts *AppCreateOptions) error {
}

if opts.AppsCloneOpts != opts.CloneOpts {
log.G().Info("committing changes to apps repo...")
log.G(ctx).Info("committing changes to apps repo...")
if err = appsRepo.Persist(ctx, &git.PushOptions{CommitMsg: getCommitMsg(opts, appsfs)}); err != nil {
return fmt.Errorf("failed to push to apps repo: %w", err)
}
}

log.G().Info("committing changes to gitops repo...")
log.G(ctx).Info("committing changes to gitops repo...")
if err = r.Persist(ctx, &git.PushOptions{CommitMsg: getCommitMsg(opts, repofs)}); err != nil {
return fmt.Errorf("failed to push to gitops repo: %w", err)
}

log.G().Infof("installed application: %s", opts.AppOpts.AppName)
if opts.Timeout > 0 {
namespace, err := getInstallationNamespace(opts.CloneOpts.FS)
if err != nil {
return fmt.Errorf("failed to get application namespace: %w", err)
}

log.G(ctx).WithField("timeout", opts.Timeout).Infof("Waiting for '%s' to finish syncing", opts.AppOpts.AppName)
fullName := fmt.Sprintf("%s-%s", opts.ProjectName, opts.AppOpts.AppName)
// wait for argocd to be ready before applying argocd-apps
stop := util.WithSpinner(ctx, fmt.Sprintf("waiting for '%s' to be ready", fullName))
if err = waitAppSynced(ctx, opts.KubeFactory, opts.Timeout, fullName, namespace); err != nil {
stop()
return fmt.Errorf("failed waiting for application to sync: %w", err)
}

stop()
}

log.G(ctx).Infof("installed application: %s", opts.AppOpts.AppName)
return nil
}

Expand Down Expand Up @@ -223,7 +256,7 @@ var setAppOptsDefaults = func(ctx context.Context, repofs fs.FS, opts *AppCreate
} else {
host, orgRepo, p, _, _, suffix, _ := util.ParseGitUrl(opts.AppOpts.AppSpecifier)
url := host + orgRepo + suffix
log.G().Infof("cloning repo: '%s', to infer app type from path '%s'", url, p)
log.G(ctx).Infof("cloning repo: '%s', to infer app type from path '%s'", url, p)
cloneOpts := &git.CloneOptions{
Repo: opts.AppOpts.AppSpecifier,
Auth: opts.CloneOpts.Auth,
Expand All @@ -237,7 +270,7 @@ var setAppOptsDefaults = func(ctx context.Context, repofs fs.FS, opts *AppCreate
}

opts.AppOpts.AppType = application.InferAppType(fsys)
log.G().Infof("inferred application type: %s", opts.AppOpts.AppType)
log.G(ctx).Infof("inferred application type: %s", opts.AppOpts.AppType)

return nil
}
Expand Down Expand Up @@ -265,6 +298,20 @@ func getCommitMsg(opts *AppCreateOptions, repofs fs.FS) string {
return commitMsg
}

func waitAppSynced(ctx context.Context, f kube.Factory, timeout time.Duration, appName, namespace string) error {
return f.Wait(ctx, &kube.WaitOptions{
Interval: store.Default.WaitInterval,
Timeout: timeout,
Resources: []kube.Resource{
{
Name: appName,
Namespace: namespace,
WaitFunc: argocd.CheckAppSynced,
},
},
})
}

func NewAppListCommand(cloneOpts *git.CloneOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "list [PROJECT_NAME]",
Expand All @@ -286,11 +333,12 @@ func NewAppListCommand(cloneOpts *git.CloneOptions) *cobra.Command {
`),
PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() },
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if len(args) < 1 {
log.G().Fatal("must enter a project name")
log.G(ctx).Fatal("must enter a project name")
}

return RunAppList(cmd.Context(), &AppListOptions{
return RunAppList(ctx, &AppListOptions{
CloneOpts: cloneOpts,
ProjectName: args[0],
})
Expand All @@ -309,7 +357,7 @@ func RunAppList(ctx context.Context, opts *AppListOptions) error {
// get all apps beneath apps/*/overlays/<project>
matches, err := billyUtils.Glob(repofs, repofs.Join(store.Default.AppsDir, "*", store.Default.OverlaysDir, opts.ProjectName))
if err != nil {
log.G().Fatalf("failed to run glob on %s", opts.ProjectName)
log.G(ctx).Fatalf("failed to run glob on %s", opts.ProjectName)
}

w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
Expand Down Expand Up @@ -370,15 +418,16 @@ func NewAppDeleteCommand(cloneOpts *git.CloneOptions) *cobra.Command {
`),
PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() },
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if len(args) < 1 {
log.G().Fatal("must enter application name")
log.G(ctx).Fatal("must enter application name")
}

if projectName == "" && !global {
log.G().Fatal("must enter project name OR use '--global' flag")
log.G(ctx).Fatal("must enter project name OR use '--global' flag")
}

return RunAppDelete(cmd.Context(), &AppDeleteOptions{
return RunAppDelete(ctx, &AppDeleteOptions{
CloneOpts: cloneOpts,
ProjectName: projectName,
AppName: args[0],
Expand Down Expand Up @@ -440,7 +489,7 @@ func RunAppDelete(ctx context.Context, opts *AppDeleteOptions) error {
return fmt.Errorf("failed to delete directory '%s': %w", dirToRemove, err)
}

log.G().Info("committing changes to gitops repo...")
log.G(ctx).Info("committing changes to gitops repo...")
if err = r.Persist(ctx, &git.PushOptions{CommitMsg: commitMsg}); err != nil {
return fmt.Errorf("failed to push to repo: %w", err)
}
Expand Down
Loading

0 comments on commit 6ea3c29

Please sign in to comment.