diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 3de0ab717766..1e453392143c 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -13,7 +13,7 @@ "info": { "description": "You can get examples of requests and responses by using the CLI with `--gloglevel=9`, e.g. `argo list --gloglevel=9`", "title": "Argo Server API", - "version": "v2.12.10" + "version": "v2.12.11" }, "paths": { "/api/v1/archived-workflows": { diff --git a/cmd/argo/commands/server.go b/cmd/argo/commands/server.go index aa2fcdb53ea1..9951efcfb88e 100644 --- a/cmd/argo/commands/server.go +++ b/cmd/argo/commands/server.go @@ -15,6 +15,7 @@ import ( "golang.org/x/net/context" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" + "k8s.io/utils/env" "github.com/argoproj/argo/v2/cmd/argo/commands/client" wfclientset "github.com/argoproj/argo/v2/pkg/client/clientset/versioned" @@ -81,8 +82,13 @@ See %s`, help.ArgoSever), if secure { cer, err := tls.LoadX509KeyPair("argo-server.crt", "argo-server.key") errors.CheckError(err) - // InsecureSkipVerify will not impact the TLS listener. It is needed for the server to speak to itself for GRPC. - tlsConfig = &tls.Config{Certificates: []tls.Certificate{cer}, InsecureSkipVerify: true} + tlsMinVersion, err := env.GetInt("TLS_MIN_VERSION", tls.VersionTLS12) + errors.CheckError(err) + tlsConfig = &tls.Config{ + Certificates: []tls.Certificate{cer}, + InsecureSkipVerify: true, // InsecureSkipVerify will not impact the TLS listener. It is needed for the server to speak to itself for GRPC. + MinVersion: uint16(tlsMinVersion), + } } else { log.Warn("You are running in insecure mode. Learn how to enable transport layer security: https://argoproj.github.io/argo/tls/") } diff --git a/cmd/workflow-controller/main.go b/cmd/workflow-controller/main.go index a4a79a198daf..a24d4cfb2fde 100644 --- a/cmd/workflow-controller/main.go +++ b/cmd/workflow-controller/main.go @@ -12,6 +12,7 @@ import ( "github.com/argoproj/pkg/stats" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + runtimeutil "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes" // load authentication plugin for obtaining credentials from cloud providers. @@ -51,6 +52,8 @@ func NewRootCommand() *cobra.Command { Use: CLIName, Short: "workflow-controller is the controller to operate on workflows", RunE: func(c *cobra.Command, args []string) error { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) + cli.SetLogLevel(logLevel) cli.SetGLogLevel(glogLevel) stats.RegisterStackDumper() diff --git a/config/controller.go b/config/controller.go index 045eff05da78..b3e5a7bcb706 100644 --- a/config/controller.go +++ b/config/controller.go @@ -9,6 +9,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + runtimeutil "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" @@ -74,6 +75,8 @@ func (cc *controller) parseConfigMap(cm *apiv1.ConfigMap) (interface{}, error) { } func (cc *controller) Run(stopCh <-chan struct{}, onChange func(config interface{}) error) { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) + restClient := cc.kubeclientset.CoreV1().RESTClient() resource := "configmaps" fieldSelector := fields.ParseSelectorOrDie(fmt.Sprintf("metadata.name=%s", cc.configMap)) diff --git a/docs/tls.md b/docs/tls.md index 7dd949e4e745..da0519ea686b 100644 --- a/docs/tls.md +++ b/docs/tls.md @@ -4,21 +4,22 @@ > v2.8 and after -If you're running Argo Server you have three options with increasing transport security (note - you should also be running [authentication](argo-server.md#auth-mode)): +If you're running Argo Server you have three options with increasing transport security (note - you should also be +running [authentication](argo-server.md#auth-mode)): ## Plain Text -*Recommended for: dev* +*Recommended for: dev* -This is the default setting: everything is sent in plain text. +This is the default setting: everything is sent in plain text. To secure the UI you may front it with a HTTPS proxy. -## Encrypted +## Encrypted *Recommended for: development and test environments* -You can encrypt connections without any real effort. +You can encrypt connections without any real effort. Start Argo Server with the `--secure` flag, e.g.: @@ -40,7 +41,8 @@ export ARGO_INSECURE_SKIP_VERIFY=true argo --secure --insecure-skip-verify list ``` -Tip: Don't forget to update your readiness probe to use HTTPS. To do so, edit your `argo-server` Deployment's `readinessProbe` spec: +Tip: Don't forget to update your readiness probe to use HTTPS. To do so, edit your `argo-server` +Deployment's `readinessProbe` spec: ``` readinessProbe: @@ -52,7 +54,8 @@ readinessProbe: *Recommended for: production environments* -Run your HTTPS proxy in front of the Argo Server. You'll need to set-up your certificates and this out of scope of this documentation. +Run your HTTPS proxy in front of the Argo Server. You'll need to set-up your certificates and this out of scope of this +documentation. Start Argo Server with the `--secure` flag, e.g.: @@ -72,3 +75,17 @@ argo --secure list export ARGO_SECURE=true argo list ``` + +### TLS Min Version + +Set `TLS_MIN_VERSION` to be the minimum TLS version to use. This is v1.2 by default. + +This must be one of these [int values](https://golang.org/pkg/crypto/tls/). + +| Version | Value | +|---|---| +| v1.0 | 769 | +| v1.1 | 770 | +| v1.2 | 771 | +| v1.3 | 772 | + diff --git a/go.mod b/go.mod index 392eae41d4d6..6c7cbc39f365 100644 --- a/go.mod +++ b/go.mod @@ -75,7 +75,7 @@ require ( k8s.io/client-go v0.17.8 k8s.io/code-generator v0.17.5 k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 - k8s.io/utils v0.0.0-20200327001022-6496210b90e8 + k8s.io/utils v0.0.0-20210305010621-2afb4311ab10 sigs.k8s.io/controller-tools v0.3.0 sigs.k8s.io/yaml v1.2.0 upper.io/db.v3 v3.6.3+incompatible diff --git a/go.sum b/go.sum index 640231649612..ffb43397ac40 100644 --- a/go.sum +++ b/go.sum @@ -204,6 +204,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -1143,14 +1144,16 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d h1:jocF7XFucw2pEiv2wS7wk2FRFCjDFGV1oa4TMs0SAT0= k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU= k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 h1:NeQXVJ2XFSkRoPzRo8AId01ZER+j8oV4SZADT4iBOXQ= k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200327001022-6496210b90e8 h1:6JFbaLjRyBz8K2Jvt+pcT+N3vvwMZfg8MfVENwe9aag= -k8s.io/utils v0.0.0-20200327001022-6496210b90e8/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20210305010621-2afb4311ab10 h1:u5rPykqiCpL+LBfjRkXvnK71gOgIdmq3eHUEkPrbeTI= +k8s.io/utils v0.0.0-20210305010621-2afb4311ab10/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= diff --git a/manifests/base/argo-server/argo-server-deployment.yaml b/manifests/base/argo-server/argo-server-deployment.yaml index 71c0db368c06..d9e89ab5b3d1 100644 --- a/manifests/base/argo-server/argo-server-deployment.yaml +++ b/manifests/base/argo-server/argo-server-deployment.yaml @@ -14,7 +14,7 @@ spec: serviceAccountName: argo-server containers: - name: argo-server - image: argoproj/argocli:v2.12.10 + image: argoproj/argocli:v2.12.11 args: [ server ] ports: - name: web diff --git a/manifests/base/workflow-controller/workflow-controller-deployment.yaml b/manifests/base/workflow-controller/workflow-controller-deployment.yaml index 1b7dc4b12113..39900de60b74 100644 --- a/manifests/base/workflow-controller/workflow-controller-deployment.yaml +++ b/manifests/base/workflow-controller/workflow-controller-deployment.yaml @@ -14,14 +14,14 @@ spec: serviceAccountName: argo containers: - name: workflow-controller - image: argoproj/workflow-controller:v2.12.10 + image: argoproj/workflow-controller:v2.12.11 command: - workflow-controller args: - --configmap - workflow-controller-configmap - --executor-image - - argoproj/argoexec:v2.12.10 + - argoproj/argoexec:v2.12.11 ports: - name: metrics containerPort: 9090 diff --git a/manifests/install.yaml b/manifests/install.yaml index 05eeb3429d39..04920d6b2684 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -459,7 +459,7 @@ spec: containers: - args: - server - image: argoproj/argocli:v2.12.10 + image: argoproj/argocli:v2.12.11 name: argo-server ports: - containerPort: 2746 @@ -501,10 +501,10 @@ spec: - --configmap - workflow-controller-configmap - --executor-image - - argoproj/argoexec:v2.12.10 + - argoproj/argoexec:v2.12.11 command: - workflow-controller - image: argoproj/workflow-controller:v2.12.10 + image: argoproj/workflow-controller:v2.12.11 livenessProbe: httpGet: path: /metrics diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 79e0d846fa90..f090a37d0ca3 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -353,7 +353,7 @@ spec: - args: - server - --namespaced - image: argoproj/argocli:v2.12.10 + image: argoproj/argocli:v2.12.11 name: argo-server ports: - containerPort: 2746 @@ -395,11 +395,11 @@ spec: - --configmap - workflow-controller-configmap - --executor-image - - argoproj/argoexec:v2.12.10 + - argoproj/argoexec:v2.12.11 - --namespaced command: - workflow-controller - image: argoproj/workflow-controller:v2.12.10 + image: argoproj/workflow-controller:v2.12.11 livenessProbe: httpGet: path: /metrics diff --git a/manifests/quick-start-minimal.yaml b/manifests/quick-start-minimal.yaml index 1cde36d161c6..d04b998c423d 100644 --- a/manifests/quick-start-minimal.yaml +++ b/manifests/quick-start-minimal.yaml @@ -591,7 +591,7 @@ spec: - server - --auth-mode - client - image: argoproj/argocli:v2.12.10 + image: argoproj/argocli:v2.12.11 name: argo-server ports: - containerPort: 2746 @@ -633,11 +633,11 @@ spec: - --configmap - workflow-controller-configmap - --executor-image - - argoproj/argoexec:v2.12.10 + - argoproj/argoexec:v2.12.11 - --namespaced command: - workflow-controller - image: argoproj/workflow-controller:v2.12.10 + image: argoproj/workflow-controller:v2.12.11 livenessProbe: httpGet: path: /metrics diff --git a/manifests/quick-start-mysql.yaml b/manifests/quick-start-mysql.yaml index eb68f03dc24c..e00624b1394b 100644 --- a/manifests/quick-start-mysql.yaml +++ b/manifests/quick-start-mysql.yaml @@ -635,7 +635,7 @@ spec: - server - --auth-mode - client - image: argoproj/argocli:v2.12.10 + image: argoproj/argocli:v2.12.11 name: argo-server ports: - containerPort: 2746 @@ -722,11 +722,11 @@ spec: - --configmap - workflow-controller-configmap - --executor-image - - argoproj/argoexec:v2.12.10 + - argoproj/argoexec:v2.12.11 - --namespaced command: - workflow-controller - image: argoproj/workflow-controller:v2.12.10 + image: argoproj/workflow-controller:v2.12.11 livenessProbe: httpGet: path: /metrics diff --git a/manifests/quick-start-postgres.yaml b/manifests/quick-start-postgres.yaml index 394ff7c5f24a..2e61ac574311 100644 --- a/manifests/quick-start-postgres.yaml +++ b/manifests/quick-start-postgres.yaml @@ -635,7 +635,7 @@ spec: - server - --auth-mode - client - image: argoproj/argocli:v2.12.10 + image: argoproj/argocli:v2.12.11 name: argo-server ports: - containerPort: 2746 @@ -714,11 +714,11 @@ spec: - --configmap - workflow-controller-configmap - --executor-image - - argoproj/argoexec:v2.12.10 + - argoproj/argoexec:v2.12.11 - --namespaced command: - workflow-controller - image: argoproj/workflow-controller:v2.12.10 + image: argoproj/workflow-controller:v2.12.11 livenessProbe: httpGet: path: /metrics diff --git a/workflow/controller/controller.go b/workflow/controller/controller.go index 9e883473a648..5674b1d64652 100644 --- a/workflow/controller/controller.go +++ b/workflow/controller/controller.go @@ -21,6 +21,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/types" + runtimeutil "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/dynamic" @@ -152,6 +153,7 @@ func (wfc *WorkflowController) newThrottler() sync.Throttler { // RunTTLController runs the workflow TTL controller func (wfc *WorkflowController) runTTLController(ctx context.Context, workflowTTLWorkers int) { ttlCtrl := ttlcontroller.NewController(wfc.wfclientset, wfc.wfInformer) + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) err := ttlCtrl.Run(ctx.Done(), workflowTTLWorkers) if err != nil { panic(err) @@ -160,6 +162,7 @@ func (wfc *WorkflowController) runTTLController(ctx context.Context, workflowTTL func (wfc *WorkflowController) runCronController(ctx context.Context) { cronController := cron.NewCronController(wfc.wfclientset, wfc.dynamicInterface, wfc.namespace, wfc.GetManagedNamespace(), wfc.Config.InstanceID, wfc.metrics, wfc.eventRecorderManager) + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) cronController.Run(ctx) } @@ -173,6 +176,7 @@ var indexers = cache.Indexers{ // Run starts an Workflow resource controller func (wfc *WorkflowController) Run(ctx context.Context, wfWorkers, workflowTTLWorkers, podWorkers int) { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) defer wfc.wfQueue.ShutDown() defer wfc.podQueue.ShutDown() @@ -217,6 +221,7 @@ func (wfc *WorkflowController) Run(ctx context.Context, wfWorkers, workflowTTLWo for i := 0; i < podWorkers; i++ { go wait.Until(wfc.podWorker, time.Second, ctx.Done()) } + <-ctx.Done() } @@ -274,6 +279,8 @@ func (wfc *WorkflowController) createSynchronizationManager() error { } func (wfc *WorkflowController) runConfigMapWatcher(stopCh <-chan struct{}) { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) + retryWatcher, err := apiwatch.NewRetryWatcher("1", &cache.ListWatch{ WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { return wfc.kubeclientset.CoreV1().ConfigMaps(wfc.managedNamespace).Watch(metav1.ListOptions{}) @@ -406,6 +413,7 @@ func (wfc *WorkflowController) workflowGarbageCollector(stopCh <-chan struct{}) log.WithFields(log.Fields{"err": err, "value": value}).Fatal("Failed to parse WORKFLOW_GC_PERIOD") } } + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) log.Infof("Performing periodic GC every %v", periodicity) ticker := time.NewTicker(periodicity) for { @@ -462,6 +470,7 @@ func (wfc *WorkflowController) archivedWorkflowGarbageCollector(stopCh <-chan st log.WithFields(log.Fields{"err": err, "value": value}).Fatal("Failed to parse ARCHIVED_WORKFLOW_GC_PERIOD") } } + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) if wfc.Config.Persistence == nil { log.Info("Persistence disabled - so archived workflow GC disabled - you must restart the controller if you enable this") return @@ -493,6 +502,7 @@ func (wfc *WorkflowController) archivedWorkflowGarbageCollector(stopCh <-chan st } func (wfc *WorkflowController) runWorker() { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) for wfc.processNextItem() { } } @@ -949,6 +959,7 @@ func (wfc *WorkflowController) isArchivable(wf *wfv1.Workflow) bool { } func (wfc *WorkflowController) syncWorkflowPhaseMetrics() { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) for _, phase := range []wfv1.NodePhase{wfv1.NodePending, wfv1.NodeRunning, wfv1.NodeSucceeded, wfv1.NodeFailed, wfv1.NodeError} { objs, err := wfc.wfInformer.GetIndexer().ByIndex(indexes.WorkflowPhaseIndex, string(phase)) if err != nil { diff --git a/workflow/controller/workflowpod.go b/workflow/controller/workflowpod.go index eb7153d1de23..0a0eda014d1b 100644 --- a/workflow/controller/workflowpod.go +++ b/workflow/controller/workflowpod.go @@ -479,16 +479,25 @@ func containerIsPrivileged(ctr *apiv1.Container) bool { } func (woc *wfOperationCtx) createEnvVars() []apiv1.EnvVar { - var execEnvVars []apiv1.EnvVar - execEnvVars = append(execEnvVars, apiv1.EnvVar{ - Name: common.EnvVarPodName, - ValueFrom: &apiv1.EnvVarSource{ - FieldRef: &apiv1.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.name", + execEnvVars := []apiv1.EnvVar{ + { + Name: common.EnvVarPodName, + ValueFrom: &apiv1.EnvVarSource{ + FieldRef: &apiv1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.name", + }, }, }, - }) + { + // This flag was introduced in Go 15 and will be removed in Go 16. + // x509: cannot validate certificate for ... because it doesn't contain any IP SANs + // https://github.com/argoproj/argo-workflows/issues/5563 - Upgrade to Go 16 + // https://github.com/golang/go/issues/39568 + Name: "GODEBUG", + Value: "x509ignoreCN=0", + }, + } if woc.controller.Config.Executor != nil { execEnvVars = append(execEnvVars, woc.controller.Config.Executor.Env...) } diff --git a/workflow/cron/controller.go b/workflow/cron/controller.go index b293fcb75f52..ebf1a2bf6eac 100644 --- a/workflow/cron/controller.go +++ b/workflow/cron/controller.go @@ -16,6 +16,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/types" + runtimeutil "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic/dynamicinformer" @@ -71,6 +72,7 @@ func NewCronController(wfclientset versioned.Interface, dynamicInterface dynamic } func (cc *Controller) Run(ctx context.Context) { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) defer cc.cronWfQueue.ShutDown() defer cc.wfQueue.ShutDown() log.Infof("Starting CronWorkflow controller") @@ -109,6 +111,8 @@ func (cc *Controller) runCronWorker() { } func (cc *Controller) processNextCronItem() bool { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) + key, quit := cc.cronWfQueue.Get() if quit { return false @@ -207,6 +211,7 @@ func (cc *Controller) addCronWorkflowInformerHandler() { } func (cc *Controller) syncAll() { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) log.Debug("Syncing all CronWorkflows") workflows, err := cc.wfLister.List() diff --git a/workflow/metrics/server.go b/workflow/metrics/server.go index d6223fb9c06a..68558077ab1f 100644 --- a/workflow/metrics/server.go +++ b/workflow/metrics/server.go @@ -9,10 +9,13 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" + runtimeutil "k8s.io/apimachinery/pkg/util/runtime" ) // RunServer starts a metrics server func (m *Metrics) RunServer(ctx context.Context) { + defer runtimeutil.HandleCrash(runtimeutil.PanicHandlers...) + if !m.metricsConfig.Enabled { // If metrics aren't enabled, return return diff --git a/workflow/validate/validate.go b/workflow/validate/validate.go index 4ae332e32ab1..19531fd9aa6f 100644 --- a/workflow/validate/validate.go +++ b/workflow/validate/validate.go @@ -621,7 +621,7 @@ func resolveAllVariables(scope map[string]interface{}, tmplStr string) error { var unresolvedErr error _, allowAllItemRefs := scope[anyItemMagicValue] // 'item.*' is a magic placeholder value set by addItemsToScope _, allowAllWorkflowOutputParameterRefs := scope[anyWorkflowOutputParameterMagicValue] - _, allowAllWorkflowOutputArtifactRefs := scope[anyWorkflowOutputArtifactMagicValue] + //_, allowAllWorkflowOutputArtifactRefs := scope[anyWorkflowOutputArtifactMagicValue] fstTmpl, err := fasttemplate.NewTemplate(tmplStr, "{{", "}}") if err != nil { return fmt.Errorf("unable to parse argo varaible: %w", err) @@ -640,7 +640,10 @@ func resolveAllVariables(scope map[string]interface{}, tmplStr string) error { // NOTE: this is far from foolproof. } else if strings.HasPrefix(tag, "workflow.outputs.parameters.") && allowAllWorkflowOutputParameterRefs { // Allow runtime resolution of workflow output parameter names - } else if strings.HasPrefix(tag, "workflow.outputs.artifacts.") && allowAllWorkflowOutputArtifactRefs { + // TODO: allow all global artifacts. The downside of it is that some step can expect global artifacts, + // but there is no validation that the step which produce this global artifacts + // is executed before the step which consume it. + } else if strings.HasPrefix(tag, "workflow.outputs.artifacts.") { // Allow runtime resolution of workflow output artifact names } else if strings.HasPrefix(tag, "outputs.") { // We are self referencing for metric emission, allow it. @@ -844,22 +847,24 @@ func (ctx *templateValidationCtx) validateSteps(scope map[string]interface{}, tm if err != nil { return errors.InternalWrapError(err) } - err = resolveAllVariables(scope, string(stepBytes)) - if err != nil { - return errors.Errorf(errors.CodeBadRequest, "templates.%s.steps %s", tmpl.Name, err.Error()) - } for _, step := range stepGroup.Steps { aggregate := len(step.WithItems) > 0 || step.WithParam != "" resolvedTmpl := resolvedTemplates[step.Name] ctx.addOutputsToScope(resolvedTmpl, fmt.Sprintf("steps.%s", step.Name), scope, aggregate, false) - // Validate the template again with actual arguments. _, err = ctx.validateTemplateHolder(&step, tmplCtx, &step.Arguments) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.steps[%d].%s %s", tmpl.Name, i, step.Name, err.Error()) } } + + // TODO: here we do not have a full scope and the global artifacts are not available + err = resolveAllVariables(scope, string(stepBytes)) + if err != nil { + return errors.Errorf(errors.CodeBadRequest, "templates.%s.steps %s", tmpl.Name, err.Error()) + } + } return nil } diff --git a/workflow/validate/validate_test.go b/workflow/validate/validate_test.go index bdf6e5e935f6..99746916f35e 100644 --- a/workflow/validate/validate_test.go +++ b/workflow/validate/validate_test.go @@ -552,11 +552,63 @@ spec: args: ["Global 1: {{workflow.parameters.message1}} Input 1: {{inputs.parameters.message1}} Input 2/Steps Input 1/Global 1: {{inputs.parameters.message2}} Input 3/Global 2: {{inputs.parameters.message3}} Input4/Steps Input 2 internal/Global 1: {{inputs.parameters.message4}}"] ` +var globalArtifactsInNestedSteps = ` +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + name: global-output +spec: + entrypoint: global-output + templates: + - name: global-output + steps: + - - name: nested + template: nested-level1 + - - name: consume-global + template: consume-global + arguments: + artifacts: + - name: art + from: "{{workflow.outputs.artifacts.global-art}}" + + - name: nested-level1 + steps: + - - name: nested + template: output-global + + - name: output-global + container: + image: alpine:3.7 + command: [sh, -c] + args: ["sleep 1; echo -n art > /tmp/art.txt; echo -n param > /tmp/param.txt"] + outputs: + artifacts: + - name: hello-art + path: /tmp/art.txt + globalName: global-art + + - name: consume-global + inputs: + artifacts: + - name: art + path: /art + container: + image: alpine:3.7 + command: [sh, -c] + args: ["cat /art"] +` + func TestGlobalParam(t *testing.T) { _, err := validate(globalParam) assert.NoError(t, err) } + +func TestGlobalParamInNestedSteps(t *testing.T) { + _, err := validate(globalArtifactsInNestedSteps) + assert.NoError(t, err) +} + var invalidTemplateNames = ` apiVersion: argoproj.io/v1alpha1 kind: Workflow