diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 17bf45108f47..d5c915f4987c 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -784,8 +784,142 @@ } } }, - "io.argoproj.workflow.v1alpha1.Sidecar": { - "description": "Sidecar is a container which runs alongside the main container", + "io.argoproj.workflow.v1alpha1.SuspendTemplate": { + "description": "SuspendTemplate is a template subtype to suspend a workflow at a predetermined point in time" + }, + "io.argoproj.workflow.v1alpha1.TarStrategy": { + "description": "TarStrategy will tar and gzip the file or directory when saving" + }, + "io.argoproj.workflow.v1alpha1.Template": { + "description": "Template is a reusable and composable unit of execution in a workflow", + "required": [ + "name" + ], + "properties": { + "activeDeadlineSeconds": { + "description": "Optional duration in seconds relative to the StartTime that the pod may be active on a node before the system actively tries to terminate the pod; value must be positive integer This field is only applicable to container and script templates.", + "type": "integer", + "format": "int64" + }, + "affinity": { + "description": "Affinity sets the pod's scheduling constraints Overrides the affinity set at the workflow level (if any)", + "$ref": "#/definitions/io.k8s.api.core.v1.Affinity" + }, + "archiveLocation": { + "description": "Location in which all files related to the step will be stored (logs, artifacts, etc...). Can be overridden by individual items in Outputs. If omitted, will use the default artifact repository location configured in the controller, appended with the \u003cworkflowname\u003e/\u003cnodename\u003e in the key.", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.ArtifactLocation" + }, + "container": { + "description": "Container is the main container image to run in the pod", + "$ref": "#/definitions/io.k8s.api.core.v1.Container" + }, + "daemon": { + "description": "Deamon will allow a workflow to proceed to the next step so long as the container reaches readiness", + "type": "boolean" + }, + "dag": { + "description": "DAG template subtype which runs a DAG", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.DAGTemplate" + }, + "initContainers": { + "description": "InitContainers is a list of containers which run before the main container.", + "type": "array", + "items": { + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.UserContainer" + } + }, + "inputs": { + "description": "Inputs describe what inputs parameters and artifacts are supplied to this template", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Inputs" + }, + "metadata": { + "description": "Metdata sets the pods's metadata, i.e. annotations and labels", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Metadata" + }, + "name": { + "description": "Name is the name of the template", + "type": "string" + }, + "nodeSelector": { + "description": "NodeSelector is a selector to schedule this step of the workflow to be run on the selected node(s). Overrides the selector set at the workflow level.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "outputs": { + "description": "Outputs describe the parameters and artifacts that this template produces", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Outputs" + }, + "parallelism": { + "description": "Parallelism limits the max total parallel pods that can execute at the same time within the boundaries of this template invocation. If additional steps/dag templates are invoked, the pods created by those templates will not be counted towards this total.", + "type": "integer", + "format": "int64" + }, + "priority": { + "description": "Priority to apply to workflow pods.", + "type": "integer", + "format": "int32" + }, + "priorityClassName": { + "description": "PriorityClassName to apply to workflow pods.", + "type": "string" + }, + "resource": { + "description": "Resource template subtype which can run k8s resources", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.ResourceTemplate" + }, + "retryStrategy": { + "description": "RetryStrategy describes how to retry a template when it fails", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.RetryStrategy" + }, + "schedulerName": { + "description": "If specified, the pod will be dispatched by specified scheduler. Or it will be dispatched by workflow scope scheduler if specified. If neither specified, the pod will be dispatched by default scheduler.", + "type": "string" + }, + "script": { + "description": "Script runs a portion of code against an interpreter", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.ScriptTemplate" + }, + "sidecars": { + "description": "Sidecars is a list of containers which run alongside the main container Sidecars are automatically killed when the main container completes", + "type": "array", + "items": { + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.UserContainer" + } + }, + "steps": { + "description": "Steps define a series of sequential/parallel workflow steps", + "type": "array", + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.WorkflowStep" + } + } + }, + "suspend": { + "description": "Suspend template subtype which can suspend a workflow when reaching the step", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.SuspendTemplate" + }, + "tolerations": { + "description": "Tolerations to apply to workflow pods.", + "type": "array", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Toleration" + } + }, + "volumes": { + "description": "Volumes is a list of volumes that can be mounted by containers in a template.", + "type": "array", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Volume" + } + } + } + }, + "io.argoproj.workflow.v1alpha1.UserContainer": { + "description": "UserContainer is a container specified by a user.", "required": [ "name" ], @@ -837,7 +971,7 @@ "$ref": "#/definitions/io.k8s.api.core.v1.Probe" }, "mirrorVolumeMounts": { - "description": "MirrorVolumeMounts will mount the same volumes specified in the main container to the sidecar (including artifacts), at the same mountPaths. This enables dind daemon to partially see the same filesystem as the main container in order to use features such as docker volume binding", + "description": "MirrorVolumeMounts will mount the same volumes specified in the main container to the container (including artifacts), at the same mountPaths. This enables dind daemon to partially see the same filesystem as the main container in order to use features such as docker volume binding", "type": "boolean" }, "name": { @@ -909,126 +1043,6 @@ } } }, - "io.argoproj.workflow.v1alpha1.SuspendTemplate": { - "description": "SuspendTemplate is a template subtype to suspend a workflow at a predetermined point in time" - }, - "io.argoproj.workflow.v1alpha1.TarStrategy": { - "description": "TarStrategy will tar and gzip the file or directory when saving" - }, - "io.argoproj.workflow.v1alpha1.Template": { - "description": "Template is a reusable and composable unit of execution in a workflow", - "required": [ - "name" - ], - "properties": { - "activeDeadlineSeconds": { - "description": "Optional duration in seconds relative to the StartTime that the pod may be active on a node before the system actively tries to terminate the pod; value must be positive integer This field is only applicable to container and script templates.", - "type": "integer", - "format": "int64" - }, - "affinity": { - "description": "Affinity sets the pod's scheduling constraints Overrides the affinity set at the workflow level (if any)", - "$ref": "#/definitions/io.k8s.api.core.v1.Affinity" - }, - "archiveLocation": { - "description": "Location in which all files related to the step will be stored (logs, artifacts, etc...). Can be overridden by individual items in Outputs. If omitted, will use the default artifact repository location configured in the controller, appended with the \u003cworkflowname\u003e/\u003cnodename\u003e in the key.", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.ArtifactLocation" - }, - "container": { - "description": "Container is the main container image to run in the pod", - "$ref": "#/definitions/io.k8s.api.core.v1.Container" - }, - "daemon": { - "description": "Deamon will allow a workflow to proceed to the next step so long as the container reaches readiness", - "type": "boolean" - }, - "dag": { - "description": "DAG template subtype which runs a DAG", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.DAGTemplate" - }, - "inputs": { - "description": "Inputs describe what inputs parameters and artifacts are supplied to this template", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Inputs" - }, - "metadata": { - "description": "Metdata sets the pods's metadata, i.e. annotations and labels", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Metadata" - }, - "name": { - "description": "Name is the name of the template", - "type": "string" - }, - "nodeSelector": { - "description": "NodeSelector is a selector to schedule this step of the workflow to be run on the selected node(s). Overrides the selector set at the workflow level.", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "outputs": { - "description": "Outputs describe the parameters and artifacts that this template produces", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Outputs" - }, - "parallelism": { - "description": "Parallelism limits the max total parallel pods that can execute at the same time within the boundaries of this template invocation. If additional steps/dag templates are invoked, the pods created by those templates will not be counted towards this total.", - "type": "integer", - "format": "int64" - }, - "priority": { - "description": "Priority to apply to workflow pods.", - "type": "integer", - "format": "int32" - }, - "priorityClassName": { - "description": "PriorityClassName to apply to workflow pods.", - "type": "string" - }, - "resource": { - "description": "Resource template subtype which can run k8s resources", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.ResourceTemplate" - }, - "retryStrategy": { - "description": "RetryStrategy describes how to retry a template when it fails", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.RetryStrategy" - }, - "schedulerName": { - "description": "If specified, the pod will be dispatched by specified scheduler. Or it will be dispatched by workflow scope scheduler if specified. If neither specified, the pod will be dispatched by default scheduler.", - "type": "string" - }, - "script": { - "description": "Script runs a portion of code against an interpreter", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.ScriptTemplate" - }, - "sidecars": { - "description": "Sidecars is a list of containers which run alongside the main container Sidecars are automatically killed when the main container completes", - "type": "array", - "items": { - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Sidecar" - } - }, - "steps": { - "description": "Steps define a series of sequential/parallel workflow steps", - "type": "array", - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.WorkflowStep" - } - } - }, - "suspend": { - "description": "Suspend template subtype which can suspend a workflow when reaching the step", - "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.SuspendTemplate" - }, - "tolerations": { - "description": "Tolerations to apply to workflow pods.", - "type": "array", - "items": { - "$ref": "#/definitions/io.k8s.api.core.v1.Toleration" - } - } - } - }, "io.argoproj.workflow.v1alpha1.ValueFrom": { "description": "ValueFrom describes a location in which to obtain the value to a parameter", "properties": { diff --git a/examples/init-container.yaml b/examples/init-container.yaml new file mode 100644 index 000000000000..a113fce55f18 --- /dev/null +++ b/examples/init-container.yaml @@ -0,0 +1,22 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: init-container- +spec: + entrypoint: init-container-example + templates: + - name: init-container-example + container: + image: alpine:latest + command: ["echo", "bye"] + volumeMounts: + - name: foo + mountPath: /foo + initContainers: + - name: hello + image: alpine:latest + command: ["echo", "hello"] + mirrorVolumeMounts: true + volumes: + - name: foo + emptyDir: diff --git a/pkg/apis/workflow/v1alpha1/openapi_generated.go b/pkg/apis/workflow/v1alpha1/openapi_generated.go index ade406b7685e..15bcaf3e1a13 100644 --- a/pkg/apis/workflow/v1alpha1/openapi_generated.go +++ b/pkg/apis/workflow/v1alpha1/openapi_generated.go @@ -40,10 +40,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.S3Bucket": schema_pkg_apis_workflow_v1alpha1_S3Bucket(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ScriptTemplate": schema_pkg_apis_workflow_v1alpha1_ScriptTemplate(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Sequence": schema_pkg_apis_workflow_v1alpha1_Sequence(ref), - "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Sidecar": schema_pkg_apis_workflow_v1alpha1_Sidecar(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SuspendTemplate": schema_pkg_apis_workflow_v1alpha1_SuspendTemplate(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.TarStrategy": schema_pkg_apis_workflow_v1alpha1_TarStrategy(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Template": schema_pkg_apis_workflow_v1alpha1_Template(ref), + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.UserContainer": schema_pkg_apis_workflow_v1alpha1_UserContainer(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ValueFrom": schema_pkg_apis_workflow_v1alpha1_ValueFrom(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Workflow": schema_pkg_apis_workflow_v1alpha1_Workflow(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowList": schema_pkg_apis_workflow_v1alpha1_WorkflowList(ref), @@ -1458,11 +1458,251 @@ func schema_pkg_apis_workflow_v1alpha1_Sequence(ref common.ReferenceCallback) co } } -func schema_pkg_apis_workflow_v1alpha1_Sidecar(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_workflow_v1alpha1_SuspendTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SuspendTemplate is a template subtype to suspend a workflow at a predetermined point in time", + Properties: map[string]spec.Schema{}, + }, + }, + Dependencies: []string{}, + } +} + +func schema_pkg_apis_workflow_v1alpha1_TarStrategy(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TarStrategy will tar and gzip the file or directory when saving", + Properties: map[string]spec.Schema{}, + }, + }, + Dependencies: []string{}, + } +} + +func schema_pkg_apis_workflow_v1alpha1_Template(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Template is a reusable and composable unit of execution in a workflow", + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the template", + Type: []string{"string"}, + Format: "", + }, + }, + "inputs": { + SchemaProps: spec.SchemaProps{ + Description: "Inputs describe what inputs parameters and artifacts are supplied to this template", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Inputs"), + }, + }, + "outputs": { + SchemaProps: spec.SchemaProps{ + Description: "Outputs describe the parameters and artifacts that this template produces", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Outputs"), + }, + }, + "nodeSelector": { + SchemaProps: spec.SchemaProps{ + Description: "NodeSelector is a selector to schedule this step of the workflow to be run on the selected node(s). Overrides the selector set at the workflow level.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "affinity": { + SchemaProps: spec.SchemaProps{ + Description: "Affinity sets the pod's scheduling constraints Overrides the affinity set at the workflow level (if any)", + Ref: ref("k8s.io/api/core/v1.Affinity"), + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Metdata sets the pods's metadata, i.e. annotations and labels", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Metadata"), + }, + }, + "daemon": { + SchemaProps: spec.SchemaProps{ + Description: "Deamon will allow a workflow to proceed to the next step so long as the container reaches readiness", + Type: []string{"boolean"}, + Format: "", + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Description: "Steps define a series of sequential/parallel workflow steps", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowStep"), + }, + }, + }, + }, + }, + }, + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Description: "Container is the main container image to run in the pod", + Ref: ref("k8s.io/api/core/v1.Container"), + }, + }, + "script": { + SchemaProps: spec.SchemaProps{ + Description: "Script runs a portion of code against an interpreter", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ScriptTemplate"), + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "Resource template subtype which can run k8s resources", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ResourceTemplate"), + }, + }, + "dag": { + SchemaProps: spec.SchemaProps{ + Description: "DAG template subtype which runs a DAG", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.DAGTemplate"), + }, + }, + "suspend": { + SchemaProps: spec.SchemaProps{ + Description: "Suspend template subtype which can suspend a workflow when reaching the step", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SuspendTemplate"), + }, + }, + "volumes": { + SchemaProps: spec.SchemaProps{ + Description: "Volumes is a list of volumes that can be mounted by containers in a template.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, + "initContainers": { + SchemaProps: spec.SchemaProps{ + Description: "InitContainers is a list of containers which run before the main container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.UserContainer"), + }, + }, + }, + }, + }, + "sidecars": { + SchemaProps: spec.SchemaProps{ + Description: "Sidecars is a list of containers which run alongside the main container Sidecars are automatically killed when the main container completes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.UserContainer"), + }, + }, + }, + }, + }, + "archiveLocation": { + SchemaProps: spec.SchemaProps{ + Description: "Location in which all files related to the step will be stored (logs, artifacts, etc...). Can be overridden by individual items in Outputs. If omitted, will use the default artifact repository location configured in the controller, appended with the / in the key.", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ArtifactLocation"), + }, + }, + "activeDeadlineSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Optional duration in seconds relative to the StartTime that the pod may be active on a node before the system actively tries to terminate the pod; value must be positive integer This field is only applicable to container and script templates.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "retryStrategy": { + SchemaProps: spec.SchemaProps{ + Description: "RetryStrategy describes how to retry a template when it fails", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.RetryStrategy"), + }, + }, + "parallelism": { + SchemaProps: spec.SchemaProps{ + Description: "Parallelism limits the max total parallel pods that can execute at the same time within the boundaries of this template invocation. If additional steps/dag templates are invoked, the pods created by those templates will not be counted towards this total.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "tolerations": { + SchemaProps: spec.SchemaProps{ + Description: "Tolerations to apply to workflow pods.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Toleration"), + }, + }, + }, + }, + }, + "schedulerName": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod will be dispatched by specified scheduler. Or it will be dispatched by workflow scope scheduler if specified. If neither specified, the pod will be dispatched by default scheduler.", + Type: []string{"string"}, + Format: "", + }, + }, + "priorityClassName": { + SchemaProps: spec.SchemaProps{ + Description: "PriorityClassName to apply to workflow pods.", + Type: []string{"string"}, + Format: "", + }, + }, + "priority": { + SchemaProps: spec.SchemaProps{ + Description: "Priority to apply to workflow pods.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ArtifactLocation", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.DAGTemplate", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Inputs", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Metadata", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Outputs", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ResourceTemplate", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.RetryStrategy", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ScriptTemplate", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SuspendTemplate", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.UserContainer", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowStep", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"}, + } +} + +func schema_pkg_apis_workflow_v1alpha1_UserContainer(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Sidecar is a container which runs alongside the main container", + Description: "UserContainer is a container specified by a user.", Properties: map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ @@ -1676,7 +1916,7 @@ func schema_pkg_apis_workflow_v1alpha1_Sidecar(ref common.ReferenceCallback) com }, "mirrorVolumeMounts": { SchemaProps: spec.SchemaProps{ - Description: "MirrorVolumeMounts will mount the same volumes specified in the main container to the sidecar (including artifacts), at the same mountPaths. This enables dind daemon to partially see the same filesystem as the main container in order to use features such as docker volume binding", + Description: "MirrorVolumeMounts will mount the same volumes specified in the main container to the container (including artifacts), at the same mountPaths. This enables dind daemon to partially see the same filesystem as the main container in order to use features such as docker volume binding", Type: []string{"boolean"}, Format: "", }, @@ -1690,220 +1930,6 @@ func schema_pkg_apis_workflow_v1alpha1_Sidecar(ref common.ReferenceCallback) com } } -func schema_pkg_apis_workflow_v1alpha1_SuspendTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SuspendTemplate is a template subtype to suspend a workflow at a predetermined point in time", - Properties: map[string]spec.Schema{}, - }, - }, - Dependencies: []string{}, - } -} - -func schema_pkg_apis_workflow_v1alpha1_TarStrategy(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TarStrategy will tar and gzip the file or directory when saving", - Properties: map[string]spec.Schema{}, - }, - }, - Dependencies: []string{}, - } -} - -func schema_pkg_apis_workflow_v1alpha1_Template(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Template is a reusable and composable unit of execution in a workflow", - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name is the name of the template", - Type: []string{"string"}, - Format: "", - }, - }, - "inputs": { - SchemaProps: spec.SchemaProps{ - Description: "Inputs describe what inputs parameters and artifacts are supplied to this template", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Inputs"), - }, - }, - "outputs": { - SchemaProps: spec.SchemaProps{ - Description: "Outputs describe the parameters and artifacts that this template produces", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Outputs"), - }, - }, - "nodeSelector": { - SchemaProps: spec.SchemaProps{ - Description: "NodeSelector is a selector to schedule this step of the workflow to be run on the selected node(s). Overrides the selector set at the workflow level.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "affinity": { - SchemaProps: spec.SchemaProps{ - Description: "Affinity sets the pod's scheduling constraints Overrides the affinity set at the workflow level (if any)", - Ref: ref("k8s.io/api/core/v1.Affinity"), - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Metdata sets the pods's metadata, i.e. annotations and labels", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Metadata"), - }, - }, - "daemon": { - SchemaProps: spec.SchemaProps{ - Description: "Deamon will allow a workflow to proceed to the next step so long as the container reaches readiness", - Type: []string{"boolean"}, - Format: "", - }, - }, - "steps": { - SchemaProps: spec.SchemaProps{ - Description: "Steps define a series of sequential/parallel workflow steps", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowStep"), - }, - }, - }, - }, - }, - }, - }, - }, - "container": { - SchemaProps: spec.SchemaProps{ - Description: "Container is the main container image to run in the pod", - Ref: ref("k8s.io/api/core/v1.Container"), - }, - }, - "script": { - SchemaProps: spec.SchemaProps{ - Description: "Script runs a portion of code against an interpreter", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ScriptTemplate"), - }, - }, - "resource": { - SchemaProps: spec.SchemaProps{ - Description: "Resource template subtype which can run k8s resources", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ResourceTemplate"), - }, - }, - "dag": { - SchemaProps: spec.SchemaProps{ - Description: "DAG template subtype which runs a DAG", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.DAGTemplate"), - }, - }, - "suspend": { - SchemaProps: spec.SchemaProps{ - Description: "Suspend template subtype which can suspend a workflow when reaching the step", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SuspendTemplate"), - }, - }, - "sidecars": { - SchemaProps: spec.SchemaProps{ - Description: "Sidecars is a list of containers which run alongside the main container Sidecars are automatically killed when the main container completes", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Sidecar"), - }, - }, - }, - }, - }, - "archiveLocation": { - SchemaProps: spec.SchemaProps{ - Description: "Location in which all files related to the step will be stored (logs, artifacts, etc...). Can be overridden by individual items in Outputs. If omitted, will use the default artifact repository location configured in the controller, appended with the / in the key.", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ArtifactLocation"), - }, - }, - "activeDeadlineSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "Optional duration in seconds relative to the StartTime that the pod may be active on a node before the system actively tries to terminate the pod; value must be positive integer This field is only applicable to container and script templates.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "retryStrategy": { - SchemaProps: spec.SchemaProps{ - Description: "RetryStrategy describes how to retry a template when it fails", - Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.RetryStrategy"), - }, - }, - "parallelism": { - SchemaProps: spec.SchemaProps{ - Description: "Parallelism limits the max total parallel pods that can execute at the same time within the boundaries of this template invocation. If additional steps/dag templates are invoked, the pods created by those templates will not be counted towards this total.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "tolerations": { - SchemaProps: spec.SchemaProps{ - Description: "Tolerations to apply to workflow pods.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Toleration"), - }, - }, - }, - }, - }, - "schedulerName": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, the pod will be dispatched by specified scheduler. Or it will be dispatched by workflow scope scheduler if specified. If neither specified, the pod will be dispatched by default scheduler.", - Type: []string{"string"}, - Format: "", - }, - }, - "priorityClassName": { - SchemaProps: spec.SchemaProps{ - Description: "PriorityClassName to apply to workflow pods.", - Type: []string{"string"}, - Format: "", - }, - }, - "priority": { - SchemaProps: spec.SchemaProps{ - Description: "Priority to apply to workflow pods.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"name"}, - }, - }, - Dependencies: []string{ - "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ArtifactLocation", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.DAGTemplate", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Inputs", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Metadata", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Outputs", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ResourceTemplate", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.RetryStrategy", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ScriptTemplate", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Sidecar", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SuspendTemplate", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowStep", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.Toleration"}, - } -} - func schema_pkg_apis_workflow_v1alpha1_ValueFrom(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/workflow/v1alpha1/types.go b/pkg/apis/workflow/v1alpha1/types.go index 3380be49fbc6..d98622b14e2c 100644 --- a/pkg/apis/workflow/v1alpha1/types.go +++ b/pkg/apis/workflow/v1alpha1/types.go @@ -205,9 +205,15 @@ type Template struct { // Suspend template subtype which can suspend a workflow when reaching the step Suspend *SuspendTemplate `json:"suspend,omitempty"` + // Volumes is a list of volumes that can be mounted by containers in a template. + Volumes []apiv1.Volume `json:"volumes,omitempty"` + + // InitContainers is a list of containers which run before the main container. + InitContainers []UserContainer `json:"initContainers,omitempty"` + // Sidecars is a list of containers which run alongside the main container // Sidecars are automatically killed when the main container completes - Sidecars []Sidecar `json:"sidecars,omitempty"` + Sidecars []UserContainer `json:"sidecars,omitempty"` // Location in which all files related to the step will be stored (logs, artifacts, etc...). // Can be overridden by individual items in Outputs. If omitted, will use the default @@ -467,12 +473,12 @@ type Arguments struct { Artifacts []Artifact `json:"artifacts,omitempty"` } -// Sidecar is a container which runs alongside the main container -type Sidecar struct { +// UserContainer is a container specified by a user. +type UserContainer struct { apiv1.Container `json:",inline"` // MirrorVolumeMounts will mount the same volumes specified in the main container - // to the sidecar (including artifacts), at the same mountPaths. This enables + // to the container (including artifacts), at the same mountPaths. This enables // dind daemon to partially see the same filesystem as the main container in // order to use features such as docker volume binding MirrorVolumeMounts *bool `json:"mirrorVolumeMounts,omitempty"` diff --git a/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go index 60db0280d209..6e1431504fa5 100644 --- a/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go @@ -702,28 +702,6 @@ func (in *Sequence) DeepCopy() *Sequence { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Sidecar) DeepCopyInto(out *Sidecar) { - *out = *in - in.Container.DeepCopyInto(&out.Container) - if in.MirrorVolumeMounts != nil { - in, out := &in.MirrorVolumeMounts, &out.MirrorVolumeMounts - *out = new(bool) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sidecar. -func (in *Sidecar) DeepCopy() *Sidecar { - if in == nil { - return nil - } - out := new(Sidecar) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SuspendTemplate) DeepCopyInto(out *SuspendTemplate) { *out = *in @@ -817,9 +795,23 @@ func (in *Template) DeepCopyInto(out *Template) { *out = new(SuspendTemplate) **out = **in } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.InitContainers != nil { + in, out := &in.InitContainers, &out.InitContainers + *out = make([]UserContainer, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Sidecars != nil { in, out := &in.Sidecars, &out.Sidecars - *out = make([]Sidecar, len(*in)) + *out = make([]UserContainer, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -869,6 +861,28 @@ func (in *Template) DeepCopy() *Template { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserContainer) DeepCopyInto(out *UserContainer) { + *out = *in + in.Container.DeepCopyInto(&out.Container) + if in.MirrorVolumeMounts != nil { + in, out := &in.MirrorVolumeMounts, &out.MirrorVolumeMounts + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserContainer. +func (in *UserContainer) DeepCopy() *UserContainer { + if in == nil { + return nil + } + out := new(UserContainer) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ValueFrom) DeepCopyInto(out *ValueFrom) { *out = *in diff --git a/test/e2e/functional/init-container.yaml b/test/e2e/functional/init-container.yaml new file mode 120000 index 000000000000..fe78772b05ed --- /dev/null +++ b/test/e2e/functional/init-container.yaml @@ -0,0 +1 @@ +../../../examples/init-container.yaml \ No newline at end of file diff --git a/workflow/controller/workflowpod.go b/workflow/controller/workflowpod.go index 4efd896ee2f4..adf1bdf652b7 100644 --- a/workflow/controller/workflowpod.go +++ b/workflow/controller/workflowpod.go @@ -168,6 +168,13 @@ func (woc *wfOperationCtx) createWorkflowPod(nodeName string, mainCtr apiv1.Cont addExecutorStagingVolume(pod) } + // addInitContainers should be called after all volumes have been manipulated + // in the main container (in case sidecar requires volume mount mirroring) + err = addInitContainers(pod, tmpl) + if err != nil { + return nil, err + } + // addSidecars should be called after all volumes have been manipulated // in the main container (in case sidecar requires volume mount mirroring) err = addSidecars(pod, tmpl) @@ -702,31 +709,40 @@ func addExecutorStagingVolume(pod *apiv1.Pod) { } } +// addInitContainers adds all init containers to the pod spec of the step +// Optionally volume mounts from the main container to the init containers +func addInitContainers(pod *apiv1.Pod, tmpl *wfv1.Template) error { + if len(tmpl.InitContainers) == 0 { + return nil + } + mainCtr := findMainContainer(pod) + if mainCtr == nil { + panic("Unable to locate main container") + } + for _, ctr := range tmpl.InitContainers { + log.Debugf("Adding init container %s", ctr.Name) + if ctr.MirrorVolumeMounts != nil && *ctr.MirrorVolumeMounts { + mirrorVolumeMounts(mainCtr, &ctr.Container) + } + pod.Spec.InitContainers = append(pod.Spec.InitContainers, ctr.Container) + } + return nil +} + // addSidecars adds all sidecars to the pod spec of the step. // Optionally volume mounts from the main container to the sidecar func addSidecars(pod *apiv1.Pod, tmpl *wfv1.Template) error { if len(tmpl.Sidecars) == 0 { return nil } - var mainCtr *apiv1.Container - for _, ctr := range pod.Spec.Containers { - if ctr.Name != common.MainContainerName { - continue - } - mainCtr = &ctr - break - } + mainCtr := findMainContainer(pod) if mainCtr == nil { panic("Unable to locate main container") } for _, sidecar := range tmpl.Sidecars { + log.Debugf("Adding sidecar container %s", sidecar.Name) if sidecar.MirrorVolumeMounts != nil && *sidecar.MirrorVolumeMounts { - for _, volMnt := range mainCtr.VolumeMounts { - if sidecar.VolumeMounts == nil { - sidecar.VolumeMounts = make([]apiv1.VolumeMount, 0) - } - sidecar.VolumeMounts = append(sidecar.VolumeMounts, volMnt) - } + mirrorVolumeMounts(mainCtr, &sidecar.Container) } pod.Spec.Containers = append(pod.Spec.Containers, sidecar.Container) } @@ -747,3 +763,27 @@ func verifyResolvedVariables(obj interface{}) error { }) return unresolvedErr } + +// findMainContainer finds main container +func findMainContainer(pod *apiv1.Pod) *apiv1.Container { + var mainCtr *apiv1.Container + for _, ctr := range pod.Spec.Containers { + if ctr.Name != common.MainContainerName { + continue + } + mainCtr = &ctr + break + } + return mainCtr +} + +// mirrorVolumeMounts mirrors volumeMounts of source container to target container +func mirrorVolumeMounts(sourceContainer, targetContainer *apiv1.Container) { + for _, volMnt := range sourceContainer.VolumeMounts { + if targetContainer.VolumeMounts == nil { + targetContainer.VolumeMounts = make([]apiv1.VolumeMount, 0) + } + log.Debugf("Adding volume mount %v to container %v", volMnt.Name, targetContainer.Name) + targetContainer.VolumeMounts = append(targetContainer.VolumeMounts, volMnt) + } +} diff --git a/workflow/controller/workflowpod_test.go b/workflow/controller/workflowpod_test.go index 59a8750b573c..f7c031e11d36 100644 --- a/workflow/controller/workflowpod_test.go +++ b/workflow/controller/workflowpod_test.go @@ -341,3 +341,81 @@ func TestSchedulerName(t *testing.T) { assert.Nil(t, err) assert.Equal(t, pod.Spec.SchedulerName, "foo") } + +// TestInitContainers verifies the ability to set up initContainers +func TestInitContainers(t *testing.T) { + volumes := []apiv1.Volume{ + { + Name: "volume-name", + VolumeSource: apiv1.VolumeSource{ + EmptyDir: &apiv1.EmptyDirVolumeSource{}, + }, + }, + } + volumeMounts := []apiv1.VolumeMount{ + { + Name: "volume-name", + MountPath: "/test", + }, + } + mirrorVolumeMounts := true + + woc := newWoc() + woc.wf.Spec.Volumes = volumes + woc.wf.Spec.Templates[0].Container.VolumeMounts = volumeMounts + woc.wf.Spec.Templates[0].InitContainers = []wfv1.UserContainer{ + { + MirrorVolumeMounts: &mirrorVolumeMounts, + Container: apiv1.Container{ + Name: "init-foo", + }, + }, + } + + woc.executeContainer(woc.wf.Spec.Entrypoint, &woc.wf.Spec.Templates[0], "") + podName := getPodName(woc.wf) + pod, err := woc.controller.kubeclientset.CoreV1().Pods("").Get(podName, metav1.GetOptions{}) + assert.Nil(t, err) + assert.Equal(t, 1, len(pod.Spec.InitContainers)) + assert.Equal(t, "init-foo", pod.Spec.InitContainers[0].Name) +} + +// TestSidecars verifies the ability to set up sidecars +func TestSidecars(t *testing.T) { + volumes := []apiv1.Volume{ + { + Name: "volume-name", + VolumeSource: apiv1.VolumeSource{ + EmptyDir: &apiv1.EmptyDirVolumeSource{}, + }, + }, + } + volumeMounts := []apiv1.VolumeMount{ + { + Name: "volume-name", + MountPath: "/test", + }, + } + mirrorVolumeMounts := true + + woc := newWoc() + woc.wf.Spec.Volumes = volumes + woc.wf.Spec.Templates[0].Container.VolumeMounts = volumeMounts + woc.wf.Spec.Templates[0].Sidecars = []wfv1.UserContainer{ + { + MirrorVolumeMounts: &mirrorVolumeMounts, + Container: apiv1.Container{ + Name: "side-foo", + }, + }, + } + + woc.executeContainer(woc.wf.Spec.Entrypoint, &woc.wf.Spec.Templates[0], "") + podName := getPodName(woc.wf) + pod, err := woc.controller.kubeclientset.CoreV1().Pods("").Get(podName, metav1.GetOptions{}) + assert.Nil(t, err) + assert.Equal(t, 3, len(pod.Spec.Containers)) + assert.Equal(t, "main", pod.Spec.Containers[0].Name) + assert.Equal(t, "wait", pod.Spec.Containers[1].Name) + assert.Equal(t, "side-foo", pod.Spec.Containers[2].Name) +}