Repository: argoproj/argo
Pull request #3271 (2020-06-22 20:34:25)

fix: Add {{workflow.status}} to workflow-metrics


Patch:
diff --git a/Makefile b/Makefile
index 2019e9d8b..ee90b12d0 100644
--- a/Makefile
+++ b/Makefile
@@ -511,13 +511,13 @@ pre-commit: test lint codegen manifests start smoke test-api test-cli
 ifneq ($(findstring release,$(GIT_BRANCH)),)
 
 .PHONY: prepare-release
-prepare-release: clean codegen manifests
+prepare-release: check-version-warning clean codegen manifests
 	# Commit if any changes
 	git diff --quiet || git commit -am "Update manifests to $(VERSION)"
 	git tag $(VERSION)
 
 .PHONY: publish-release
-publish-release: build
+publish-release: check-version-warning build
 	# Push images to Docker Hub
 	docker push $(IMAGE_NAMESPACE)/argocli:$(VERSION)
 	docker push $(IMAGE_NAMESPACE)/argoexec:$(VERSION)
@@ -525,3 +525,7 @@ publish-release: build
 	git push
 	git push $(GIT_REMOTE) $(VERSION)
 endif
+
+.PHONY: check-version-warning
+check-version-warning:
+	@if [[ "$(VERSION)" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$  ]]; then echo -n "It looks like you're trying to use a SemVer version, but have not prepended it with a "v" (such as "v$(VERSION)"). The "v" is required for our releases. Do you wish to continue anyway? [y/N]" && read ans && [ $${ans:-N} = y ]; fi
diff --git a/ui/src/app/cluster-workflow-templates/components/cluster-workflow-template-details/cluster-workflow-template-details.tsx b/ui/src/app/cluster-workflow-templates/components/cluster-workflow-template-details/cluster-workflow-template-details.tsx
index 47b3f6066..bc98da33e 100644
--- a/ui/src/app/cluster-workflow-templates/components/cluster-workflow-template-details/cluster-workflow-template-details.tsx
+++ b/ui/src/app/cluster-workflow-templates/components/cluster-workflow-template-details/cluster-workflow-template-details.tsx
@@ -14,6 +14,7 @@ import {ClusterWorkflowTemplateSummaryPanel} from '../cluster-workflow-template-
 require('../../../workflows/components/workflow-details/workflow-details.scss');
 
 interface State {
+    namespace?: string;
     template?: models.ClusterWorkflowTemplate;
     error?: Error;
 }
@@ -37,11 +38,13 @@ export class ClusterWorkflowTemplateDetails extends BasePage<RouteComponentProps
     }
 
     public componentDidMount(): void {
+        services.info
+            .getInfo()
+            .then(info => this.setState({namespace: info.managedNamespace || 'default'}))
+            .catch(error => this.setState(error));
         services.clusterWorkflowTemplate
             .get(this.name)
-            .then(template => {
-                this.setState({template});
-            })
+            .then(template => this.setState({template}))
             .catch(error => this.setState({error}));
     }
 
@@ -127,10 +130,10 @@ export class ClusterWorkflowTemplateDetails extends BasePage<RouteComponentProps
         return {
             metadata: {
                 generateName: template.metadata.name + '-',
-                namespace: '<enter the namespace>'
+                namespace: this.state.namespace
             },
             spec: {
-                entrypoint: template.spec.templates[0].name,
+                entrypoint: !!template.spec.templates ? template.spec.templates[0].name : '',
                 workflowTemplateRef: {
                     name: template.metadata.name,
                     clusterScope: true
diff --git a/ui/src/app/workflow-templates/components/workflow-template-details/workflow-template-details.tsx b/ui/src/app/workflow-templates/components/workflow-template-details/workflow-template-details.tsx
index 6353881a5..68028459c 100644
--- a/ui/src/app/workflow-templates/components/workflow-template-details/workflow-template-details.tsx
+++ b/ui/src/app/workflow-templates/components/workflow-template-details/workflow-template-details.tsx
@@ -43,9 +43,7 @@ export class WorkflowTemplateDetails extends BasePage<RouteComponentProps<any>,
     public componentDidMount(): void {
         services.workflowTemplate
             .get(this.name, this.namespace)
-            .then(template => {
-                this.setState({template});
-            })
+            .then(template => this.setState({template}))
             .catch(error => this.setState({error}));
     }
 
@@ -134,7 +132,7 @@ export class WorkflowTemplateDetails extends BasePage<RouteComponentProps<any>,
                 namespace: template.metadata.namespace
             },
             spec: {
-                entrypoint: template.spec.templates[0].name,
+                entrypoint: !!template.spec.templates ? template.spec.templates[0].name : '',
                 workflowTemplateRef: {
                     name: template.metadata.name
                 }
diff --git a/workflow/controller/operator.go b/workflow/controller/operator.go
index 37b5babc2..e6cc449b6 100644
--- a/workflow/controller/operator.go
+++ b/workflow/controller/operator.go
@@ -399,6 +399,7 @@ func (woc *wfOperationCtx) operate() {
 		realTimeScope := map[string]func() float64{common.GlobalVarWorkflowDuration: func() float64 {
 			return node.FinishedAt.Sub(node.StartedAt.Time).Seconds()
 		}}
+		woc.globalParams[common.GlobalVarWorkflowStatus] = string(workflowStatus)
 		woc.computeMetrics(woc.wfSpec.Metrics.Prometheus, woc.globalParams, realTimeScope, false)
 	}
 }
diff --git a/workflow/executor/resource.go b/workflow/executor/resource.go
index 515a91ea2..9e64c7765 100644
--- a/workflow/executor/resource.go
+++ b/workflow/executor/resource.go
@@ -330,7 +330,7 @@ func (we *WorkflowExecutor) SaveResourceParameters(resourceNamespace string, res
 			if resourceNamespace != "" {
 				resArgs = append(resArgs, "-n", resourceNamespace)
 			}
-			cmdStr := fmt.Sprintf("kubectl get %s -o json | jq -c '%s'", strings.Join(resArgs, " "), param.ValueFrom.JQFilter)
+			cmdStr := fmt.Sprintf("kubectl get %s -o json | jq -rc '%s'", strings.Join(resArgs, " "), param.ValueFrom.JQFilter)
 			cmd = exec.Command("sh", "-c", cmdStr)
 		} else {
 			continue


Test patch:
diff --git a/test/e2e/functional/output-jqfilter-parameters.yaml b/test/e2e/functional/output-jqfilter-parameters.yaml
new file mode 100644
index 000000000..8dc0226aa
--- /dev/null
+++ b/test/e2e/functional/output-jqfilter-parameters.yaml
@@ -0,0 +1,103 @@
+apiVersion: argoproj.io/v1alpha1
+kind: Workflow
+metadata:
+  generateName: output-jqfilter-parameters-
+spec:
+  entrypoint: main
+  templates:
+
+  # main template that:
+  #   * create a configmap with json payload
+  #   * extract data using jqFilter from it
+  #   * compare the output parameters are what we expect
+  - name: main
+    steps:
+    - - name: create-param
+        template: create-param
+
+    - - name: get-param
+        template: get-param
+
+    - - name: check-param-one-value
+        template: check-param
+        arguments:
+          parameters:
+          - name: key
+            value: "{{steps.get-param.outputs.parameters.one_value}}"
+          - name: value
+            value: "foo"
+      - name: check-param-one-dict
+        template: check-param
+        arguments:
+          parameters:
+          - name: key
+            value: "{{steps.get-param.outputs.parameters.one_dict}}"
+          - name: value
+            value: |
+              {"foo":"bar"}
+      - name: check-param-one-list
+        template: check-param
+        arguments:
+          parameters:
+          - name: key
+            value: "{{steps.get-param.outputs.parameters.one_list}}"
+          - name: value
+            value: |
+              [{"foo":"bar"}]
+
+  # check that key and value parameters are aligned
+  - name: check-param
+    inputs:
+      parameters:
+      - name: key
+      - name: value
+    container:
+      image: argoproj/argoexec:latest
+      command: [sh, -c]
+      args: ["
+        echo '<{{inputs.parameters.key}}>' > /tmp/input;
+        echo '<{{inputs.parameters.value}}>' > /tmp/expected;
+        diff /tmp/input /tmp/expected;
+      "]
+
+  # extract parameters from a configmap json payload using jqFilter
+  - name: get-param
+    resource:
+      action: get
+      manifest: |
+        apiVersion: v1
+        kind: ConfigMap
+        metadata:
+          name: cm-output-jqfilter-parameters
+    outputs:
+      parameters:
+      - name: one_value
+        valueFrom:
+          jqFilter: '.data.some | fromjson | .one_value'
+      - name: one_dict
+        valueFrom:
+          jqfilter: '.data.some | fromjson | .one_dict'
+      - name: one_list
+        valueFrom:
+          jqfilter: '.data.some | fromjson | .one_list'
+
+  # create a configmap with json payload
+  - name: create-param
+    resource:
+      action: create
+      manifest: |
+        apiVersion: v1
+        kind: ConfigMap
+        metadata:
+          name: cm-output-jqfilter-parameters
+        data:
+          some: |
+            {
+              "one_value": "foo",
+              "one_dict": {"foo":"bar"},
+              "one_list": [
+                {
+                  "foo":"bar"
+                }
+              ]
+            }
diff --git a/workflow/controller/operator_test.go b/workflow/controller/operator_test.go
index 4981189ff..fec9bc9b0 100644
--- a/workflow/controller/operator_test.go
+++ b/workflow/controller/operator_test.go
@@ -3739,3 +3739,113 @@ func TestValidReferenceMode(t *testing.T) {
 	woc.operate()
 	assert.Equal(t, wfv1.NodeRunning, woc.wf.Status.Phase)
 }
+
+var workflowStatusMetric = `
+apiVersion: argoproj.io/v1alpha1
+kind: Workflow
+metadata:
+  name: retry-to-completion-rngcr
+spec:
+  arguments: {}
+  entrypoint: retry-to-completion
+  metrics:
+    prometheus:
+    - counter:
+        value: "1"
+      gauge: null
+      help: Count of step execution by result status
+      histogram: null
+      labels:
+      - key: name
+        value: retry
+      - key: status
+        value: '{{workflow.status}}'
+      name: result_counter
+      when: ""
+  templates:
+  - arguments: {}
+    container:
+      args:
+      - import random; import sys; exit_code = random.choice(range(0, 5)); sys.exit(exit_code)
+      command:
+      - python
+      - -c
+      image: python
+      name: ""
+      resources: {}
+    inputs: {}
+    metadata: {}
+    name: retry-to-completion
+    outputs: {}
+    retryStrategy: {}
+status:
+  nodes:
+    retry-to-completion-rngcr:
+      children:
+      - retry-to-completion-rngcr-1856960714
+      displayName: retry-to-completion-rngcr
+      finishedAt: "2020-06-22T20:33:10Z"
+      id: retry-to-completion-rngcr
+      name: retry-to-completion-rngcr
+      outputs:
+        artifacts:
+        - archiveLogs: true
+          name: main-logs
+          s3:
+            accessKeySecret:
+              key: accesskey
+              name: my-minio-cred
+            bucket: my-bucket
+            endpoint: minio:9000
+            insecure: true
+            key: retry-to-completion-rngcr/retry-to-completion-rngcr-4003951493/main.log
+            secretKeySecret:
+              key: secretkey
+              name: my-minio-cred
+      phase: Succeeded
+      startedAt: "2020-06-22T20:32:15Z"
+      templateName: retry-to-completion
+      templateScope: local/retry-to-completion-rngcr
+      type: Retry
+    retry-to-completion-rngcr-1856960714:
+      displayName: retry-to-completion-rngcr(0)
+      finishedAt: "2020-06-22T20:32:25Z"
+      hostNodeName: minikube
+      id: retry-to-completion-rngcr-1856960714
+      message: failed with exit code 3
+      name: retry-to-completion-rngcr(0)
+      outputs:
+        artifacts:
+        - archiveLogs: true
+          name: main-logs
+          s3:
+            accessKeySecret:
+              key: accesskey
+              name: my-minio-cred
+            bucket: my-bucket
+            endpoint: minio:9000
+            insecure: true
+            key: retry-to-completion-rngcr/retry-to-completion-rngcr-1856960714/main.log
+            secretKeySecret:
+              key: secretkey
+              name: my-minio-cred
+        exitCode: "3"
+      phase: Failed
+      resourcesDuration:
+        cpu: 10
+        memory: 6
+      startedAt: "2020-06-22T20:32:15Z"
+      templateName: retry-to-completion
+      templateScope: local/retry-to-completion-rngcr
+      type: Pod
+  phase: Running
+  startedAt: "2020-06-22T20:32:15Z"
+`
+
+func TestWorkflowStatusMetric(t *testing.T) {
+	wf := unmarshalWF(workflowStatusMetric)
+	woc := newWoc(*wf)
+	woc.operate()
+	// Must only be one (completed: true)
+	assert.Len(t, woc.wf.Status.Conditions, 1)
+}