Skip to content

Commit

Permalink
feat(cli): Add offline linting (#5569)
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Collins <alexec@users.noreply.github.com>
  • Loading branch information
NikeNano and alexec authored Apr 16, 2021
1 parent a018523 commit 8607391
Show file tree
Hide file tree
Showing 28 changed files with 304 additions and 55 deletions.
9 changes: 8 additions & 1 deletion cmd/argo/commands/client/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ var (

var overrides = clientcmd.ConfigOverrides{}

var explicitPath string
var (
explicitPath string
Offline bool
)

func AddKubectlFlagsToCmd(cmd *cobra.Command) {
kflags := clientcmd.RecommendedConfigOverrideFlags("")
Expand Down Expand Up @@ -58,6 +61,7 @@ func NewAPIClient() (context.Context, apiclient.Client) {
return GetAuthString()
},
ClientConfigSupplier: func() clientcmd.ClientConfig { return GetConfig() },
Offline: Offline,
})
if err != nil {
log.Fatal(err)
Expand All @@ -66,6 +70,9 @@ func NewAPIClient() (context.Context, apiclient.Client) {
}

func Namespace() string {
if Offline {
return ""
}
if overrides.Context.Namespace != "" {
return overrides.Context.Namespace
}
Expand Down
5 changes: 4 additions & 1 deletion cmd/argo/commands/clustertemplate/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ func createClusterWorkflowTemplates(filePaths []string, cliOpts *cliCreateOpts)
cliOpts = &cliCreateOpts{}
}
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewClusterWorkflowTemplateServiceClient()
serviceClient, err := apiClient.NewClusterWorkflowTemplateServiceClient()
if err != nil {
log.Fatal(err)
}

fileContents, err := util.ReadManifest(filePaths...)
if err != nil {
Expand Down
4 changes: 3 additions & 1 deletion cmd/argo/commands/clustertemplate/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ func NewDeleteCommand() *cobra.Command {

func apiServerDeleteClusterWorkflowTemplates(allWFs bool, wfTmplNames []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewClusterWorkflowTemplateServiceClient()
serviceClient, err := apiClient.NewClusterWorkflowTemplateServiceClient()
errors.CheckError(err)

var delWFTmplNames []string
if allWFs {
cwftmplList, err := serviceClient.ListClusterWorkflowTemplates(ctx, &clusterworkflowtemplate.ClusterWorkflowTemplateListRequest{})
Expand Down
5 changes: 4 additions & 1 deletion cmd/argo/commands/clustertemplate/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ func NewGetCommand() *cobra.Command {
Short: "display details about a cluster workflow template",
Run: func(cmd *cobra.Command, args []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewClusterWorkflowTemplateServiceClient()
serviceClient, err := apiClient.NewClusterWorkflowTemplateServiceClient()
if err != nil {
log.Fatal(err)
}
for _, name := range args {
wftmpl, err := serviceClient.GetClusterWorkflowTemplate(ctx, &clusterworkflowtmplpkg.ClusterWorkflowTemplateGetRequest{
Name: name,
Expand Down
10 changes: 9 additions & 1 deletion cmd/argo/commands/clustertemplate/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,16 @@ func NewLintCommand() *cobra.Command {
cmd.HelpFunc()(cmd, args)
os.Exit(1)
}

ctx, apiClient := client.NewAPIClient()
lint.RunLint(ctx, apiClient, args, []string{wf.ClusterWorkflowTemplatePlural}, client.Namespace(), output, strict)
opts := lint.LintOptions{
Files: args,
DefaultNamespace: client.Namespace(),
Strict: strict,
Printer: os.Stdout,
}

lint.RunLint(ctx, apiClient, []string{wf.ClusterWorkflowTemplatePlural}, output, false, opts)
},
}

Expand Down
5 changes: 4 additions & 1 deletion cmd/argo/commands/clustertemplate/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ func NewListCommand() *cobra.Command {
Short: "list cluster workflow templates",
Run: func(cmd *cobra.Command, args []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewClusterWorkflowTemplateServiceClient()
serviceClient, err := apiClient.NewClusterWorkflowTemplateServiceClient()
if err != nil {
log.Fatal(err)
}

cwftmplList, err := serviceClient.ListClusterWorkflowTemplates(ctx, &clusterworkflowtemplate.ClusterWorkflowTemplateListRequest{})
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion cmd/argo/commands/cron/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ func NewCreateCommand() *cobra.Command {

func CreateCronWorkflows(filePaths []string, cliOpts *cliCreateOpts, submitOpts *wfv1.SubmitOpts) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewCronWorkflowServiceClient()
serviceClient, err := apiClient.NewCronWorkflowServiceClient()
if err != nil {
log.Fatal(err)
}

fileContents, err := util.ReadManifest(filePaths...)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion cmd/argo/commands/cron/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func NewDeleteCommand() *cobra.Command {
Short: "delete a cron workflow",
Run: func(cmd *cobra.Command, args []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewCronWorkflowServiceClient()
serviceClient, err := apiClient.NewCronWorkflowServiceClient()
errors.CheckError(err)
if all {
cronWfList, err := serviceClient.ListCronWorkflows(ctx, &cronworkflowpkg.ListCronWorkflowsRequest{
Namespace: client.Namespace(),
Expand Down
3 changes: 2 additions & 1 deletion cmd/argo/commands/cron/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ func NewGetCommand() *cobra.Command {
}

ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewCronWorkflowServiceClient()
serviceClient, err := apiClient.NewCronWorkflowServiceClient()
errors.CheckError(err)
namespace := client.Namespace()

for _, arg := range args {
Expand Down
8 changes: 7 additions & 1 deletion cmd/argo/commands/cron/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ func NewLintCommand() *cobra.Command {
os.Exit(1)
}
ctx, apiClient := client.NewAPIClient()
lint.RunLint(ctx, apiClient, args, []string{wf.CronWorkflowPlural}, client.Namespace(), output, strict)
opts := lint.LintOptions{
Files: args,
Strict: strict,
DefaultNamespace: client.Namespace(),
Printer: os.Stdout,
}
lint.RunLint(ctx, apiClient, []string{wf.CronWorkflowPlural}, output, false, opts)
},
}

Expand Down
3 changes: 2 additions & 1 deletion cmd/argo/commands/cron/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ func NewListCommand() *cobra.Command {
Short: "list cron workflows",
Run: func(cmd *cobra.Command, args []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewCronWorkflowServiceClient()
serviceClient, err := apiClient.NewCronWorkflowServiceClient()
errors.CheckError(err)
namespace := client.Namespace()
if listArgs.allNamespaces {
namespace = ""
Expand Down
3 changes: 2 additions & 1 deletion cmd/argo/commands/cron/resume.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func NewResumeCommand() *cobra.Command {
Short: "resume zero or more cron workflows",
Run: func(cmd *cobra.Command, args []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewCronWorkflowServiceClient()
serviceClient, err := apiClient.NewCronWorkflowServiceClient()
errors.CheckError(err)
namespace := client.Namespace()
for _, name := range args {
_, err := serviceClient.ResumeCronWorkflow(ctx, &cronworkflowpkg.CronWorkflowResumeRequest{
Expand Down
3 changes: 2 additions & 1 deletion cmd/argo/commands/cron/suspend.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func NewSuspendCommand() *cobra.Command {
Short: "suspend zero or more cron workflows",
Run: func(cmd *cobra.Command, args []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewCronWorkflowServiceClient()
serviceClient, err := apiClient.NewCronWorkflowServiceClient()
errors.CheckError(err)
namespace := client.Namespace()
for _, name := range args {
cronWf, err := serviceClient.SuspendCronWorkflow(ctx, &cronworkflowpkg.CronWorkflowSuspendRequest{
Expand Down
11 changes: 10 additions & 1 deletion cmd/argo/commands/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func NewLintCommand() *cobra.Command {
strict bool
lintKinds []string
output string
offline bool
)

allKinds := []string{wf.WorkflowPlural, wf.WorkflowTemplatePlural, wf.CronWorkflowPlural, wf.ClusterWorkflowTemplatePlural}
Expand All @@ -33,6 +34,7 @@ func NewLintCommand() *cobra.Command {
cat manifests.yaml | argo lint --kinds=workflows,cronworkflows -`,
Run: func(cmd *cobra.Command, args []string) {
client.Offline = offline
ctx, apiClient := client.NewAPIClient()
if len(args) == 0 {
cmd.HelpFunc()(cmd, args)
Expand All @@ -41,13 +43,20 @@ func NewLintCommand() *cobra.Command {
if len(lintKinds) == 0 || strings.Contains(strings.Join(lintKinds, ","), "all") {
lintKinds = allKinds
}
lint.RunLint(ctx, apiClient, args, lintKinds, client.Namespace(), output, strict)
ops := lint.LintOptions{
Files: args,
Strict: strict,
DefaultNamespace: client.Namespace(),
Printer: os.Stdout,
}
lint.RunLint(ctx, apiClient, lintKinds, output, offline, ops)
},
}

command.Flags().StringSliceVar(&lintKinds, "kinds", []string{"all"}, fmt.Sprintf("Which kinds will be linted. Can be: %s", strings.Join(allKinds, "|")))
command.Flags().StringVarP(&output, "output", "o", "pretty", "Linting results output format. One of: pretty|simple")
command.Flags().BoolVar(&strict, "strict", true, "Perform strict workflow validation")
command.Flags().BoolVar(&offline, "offline", false, "perform offline linting")

return command
}
5 changes: 4 additions & 1 deletion cmd/argo/commands/template/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ func CreateWorkflowTemplates(filePaths []string, cliOpts *cliCreateOpts) {
cliOpts = &cliCreateOpts{}
}
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewWorkflowTemplateServiceClient()
serviceClient, err := apiClient.NewWorkflowTemplateServiceClient()
if err != nil {
log.Fatal(err)
}

fileContents, err := util.ReadManifest(filePaths...)
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion cmd/argo/commands/template/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ func NewDeleteCommand() *cobra.Command {

func apiServerDeleteWorkflowTemplates(allWFs bool, wfTmplNames []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewWorkflowTemplateServiceClient()
serviceClient, err := apiClient.NewWorkflowTemplateServiceClient()
if err != nil {
log.Fatal(err)
}
namespace := client.Namespace()
var delWFTmplNames []string
if allWFs {
Expand Down
5 changes: 4 additions & 1 deletion cmd/argo/commands/template/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ func NewGetCommand() *cobra.Command {
Short: "display details about a workflow template",
Run: func(cmd *cobra.Command, args []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewWorkflowTemplateServiceClient()
serviceClient, err := apiClient.NewWorkflowTemplateServiceClient()
if err != nil {
log.Fatal(err)
}
namespace := client.Namespace()
for _, name := range args {
wftmpl, err := serviceClient.GetWorkflowTemplate(ctx, &workflowtemplatepkg.WorkflowTemplateGetRequest{
Expand Down
8 changes: 7 additions & 1 deletion cmd/argo/commands/template/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ func NewLintCommand() *cobra.Command {
os.Exit(1)
}
ctx, apiClient := client.NewAPIClient()
lint.RunLint(ctx, apiClient, args, []string{wf.WorkflowTemplatePlural}, client.Namespace(), output, strict)
opts := lint.LintOptions{
Files: args,
Strict: strict,
DefaultNamespace: client.Namespace(),
Printer: os.Stdout,
}
lint.RunLint(ctx, apiClient, []string{wf.WorkflowTemplatePlural}, output, false, opts)
},
}

Expand Down
5 changes: 4 additions & 1 deletion cmd/argo/commands/template/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ func NewListCommand() *cobra.Command {
Short: "list workflow templates",
Run: func(cmd *cobra.Command, args []string) {
ctx, apiClient := client.NewAPIClient()
serviceClient := apiClient.NewWorkflowTemplateServiceClient()
serviceClient, err := apiClient.NewWorkflowTemplateServiceClient()
if err != nil {
log.Fatal(err)
}
namespace := client.Namespace()
if listArgs.allNamespaces {
namespace = apiv1.NamespaceAll
Expand Down
34 changes: 20 additions & 14 deletions cmd/argo/lint/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,21 +88,14 @@ func GetFormatter(fmtr string) (Formatter, error) {

// RunLint lints the specified kinds in the specified files and prints the results to os.Stdout.
// If linting fails it will exit with status code 1.
func RunLint(ctx context.Context, client apiclient.Client, files, kinds []string, defaultNs, output string, strict bool) {
func RunLint(ctx context.Context, client apiclient.Client, kinds []string, output string, offline bool, opts LintOptions) {
fmtr, err := GetFormatter(output)
errors.CheckError(err)

clients, err := getLintClients(client, kinds)
errors.CheckError(err)

res, err := Lint(ctx, &LintOptions{
ServiceClients: clients,
Files: files,
Strict: strict,
DefaultNamespace: defaultNs,
Formatter: fmtr,
Printer: os.Stdout,
})
opts.ServiceClients = clients
opts.Formatter = fmtr
res, err := Lint(ctx, &opts)
errors.CheckError(err)

if !res.Success {
Expand Down Expand Up @@ -314,16 +307,29 @@ func getObjectName(kind string, obj metav1.Object, objIndex int) string {

func getLintClients(client apiclient.Client, kinds []string) (ServiceClients, error) {
res := ServiceClients{}
var err error
for _, kind := range kinds {
switch kind {
case wf.WorkflowPlural, wf.WorkflowShortName:
res.WorkflowsClient = client.NewWorkflowServiceClient()
case wf.WorkflowTemplatePlural, wf.WorkflowTemplateShortName:
res.WorkflowTemplatesClient = client.NewWorkflowTemplateServiceClient()
res.WorkflowTemplatesClient, err = client.NewWorkflowTemplateServiceClient()
if err != nil {
return ServiceClients{}, err
}

case wf.CronWorkflowPlural, wf.CronWorkflowShortName:
res.CronWorkflowsClient = client.NewCronWorkflowServiceClient()
res.CronWorkflowsClient, err = client.NewCronWorkflowServiceClient()
if err != nil {
return ServiceClients{}, err
}

case wf.ClusterWorkflowTemplatePlural, wf.ClusterWorkflowTemplateShortName:
res.ClusterWorkflowTemplateClient = client.NewClusterWorkflowTemplateServiceClient()
res.ClusterWorkflowTemplateClient, err = client.NewClusterWorkflowTemplateServiceClient()
if err != nil {
return ServiceClients{}, err
}

default:
return res, fmt.Errorf("unknown kind: %s", kind)
}
Expand Down
1 change: 1 addition & 0 deletions docs/cli/argo_lint.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ argo lint FILE... [flags]
```
-h, --help help for lint
--kinds strings Which kinds will be linted. Can be: workflows|workflowtemplates|cronworkflows|clusterworkflowtemplates (default [all])
--offline perform offline linting
-o, --output string Linting results output format. One of: pretty|simple (default "pretty")
--strict Perform strict workflow validation (default true)
```
Expand Down
14 changes: 10 additions & 4 deletions pkg/apiclient/apiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import (
type Client interface {
NewArchivedWorkflowServiceClient() (workflowarchivepkg.ArchivedWorkflowServiceClient, error)
NewWorkflowServiceClient() workflowpkg.WorkflowServiceClient
NewCronWorkflowServiceClient() cronworkflowpkg.CronWorkflowServiceClient
NewWorkflowTemplateServiceClient() workflowtemplatepkg.WorkflowTemplateServiceClient
NewClusterWorkflowTemplateServiceClient() clusterworkflowtmplpkg.ClusterWorkflowTemplateServiceClient
NewCronWorkflowServiceClient() (cronworkflowpkg.CronWorkflowServiceClient, error)
NewWorkflowTemplateServiceClient() (workflowtemplatepkg.WorkflowTemplateServiceClient, error)
NewClusterWorkflowTemplateServiceClient() (clusterworkflowtmplpkg.ClusterWorkflowTemplateServiceClient, error)
NewInfoServiceClient() (infopkg.InfoServiceClient, error)
}

Expand All @@ -32,6 +32,7 @@ type Opts struct {
// DEPRECATED: use `ClientConfigSupplier`
ClientConfig clientcmd.ClientConfig
ClientConfigSupplier func() clientcmd.ClientConfig
Offline bool
}

func (o Opts) String() string {
Expand All @@ -51,6 +52,9 @@ func NewClient(argoServer string, authSupplier func() string, clientConfig clien

func NewClientFromOpts(opts Opts) (context.Context, Client, error) {
log.WithField("opts", opts).Debug("Client options")
if opts.Offline {
return newOfflineClient()
}
if opts.ArgoServerOpts.URL != "" && opts.InstanceID != "" {
return nil, nil, fmt.Errorf("cannot use instance ID with Argo Server")
}
Expand All @@ -62,6 +66,8 @@ func NewClientFromOpts(opts Opts) (context.Context, Client, error) {
if opts.ClientConfigSupplier != nil {
opts.ClientConfig = opts.ClientConfigSupplier()
}
return newArgoKubeClient(opts.ClientConfig, instanceid.NewService(opts.InstanceID))

ctx, client, err := newArgoKubeClient(opts.ClientConfig, instanceid.NewService(opts.InstanceID))
return ctx, client, err
}
}
Loading

0 comments on commit 8607391

Please sign in to comment.