Skip to content

Commit

Permalink
updates to pr #721
Browse files Browse the repository at this point in the history
- run the image digest exporter after each step
- move the index path to the task definition instead of the resource since the user needs to know this at the time of creating the task so it can be used as part of the steps scripting if needed
- rename indexpath to outputimagepath
- add example yaml
  • Loading branch information
nader-ziada committed Apr 25, 2019
1 parent 80888fa commit e3e3ee1
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 56 deletions.
2 changes: 1 addition & 1 deletion cmd/imagedigestexporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func main() {

output := []v1alpha1.PipelineResourceResult{}
for _, imageResource := range imageResources {
ii, err := layout.ImageIndexFromPath(imageResource.IndexPath)
ii, err := layout.ImageIndexFromPath(imageResource.OutputImagePath)
if err != nil {
// if this image doesn't have a builder that supports index.josn file,
// then it will be skipped
Expand Down
103 changes: 103 additions & 0 deletions examples/taskruns/task-output-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: skaffold-image-leeroy-web
spec:
type: image
params:
- name: url
value: gcr.io/christiewilson-catfactory/leeroy-web # Replace this URL with ${KO_DOCKER_REPO}
---
# This demo modifies the cluster (deploys to it) you must use a service
# account with permission to admin the cluster (or make your default user an admin
# of the `default` namespace with default-cluster-admin.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: default-cluster-admin
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: skaffold-git
spec:
type: git
params:
- name: revision
value: master
- name: url
value: https://github.com/GoogleContainerTools/skaffold
---
#Builds an image via kaniko and pushes it to registry.
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
name: build-push-kaniko
spec:
inputs:
resources:
- name: workspace
type: git
outputs:
resources:
- name: builtImage
type: image
outputImagePath: /workspace/workspace
steps:
- name: build-and-push
image: busybox
command:
- /bin/sh
args:
- -ce
- |
set -e
cat <<EOF > /workspace/workspace/index.json
{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"size": 314,
"digest": "sha256:05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5"
}
]
}
EOF
- name: echo
image: busybox
command:
- /bin/sh
args:
- -ce
- |
set -e
cat /workspace/workspace/index.json
---
apiVersion: tekton.dev/v1alpha1
kind: TaskRun
metadata:
name: build-push-run
spec:
taskRef:
name: build-push-kaniko
trigger:
type: manual
inputs:
resources:
- name: workspace
resourceRef:
name: skaffold-git
outputs:
resources:
- name: builtImage
resourceRef:
name: skaffold-image-leeroy-web
27 changes: 12 additions & 15 deletions pkg/apis/pipeline/v1alpha1/image_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ func NewImageResource(r *PipelineResource) (*ImageResource, error) {
ir.URL = param.Value
case strings.EqualFold(param.Name, "Digest"):
ir.Digest = param.Value
case strings.EqualFold(param.Name, "IndexPath"):
ir.IndexPath = param.Value
}
}

Expand All @@ -50,11 +48,11 @@ func NewImageResource(r *PipelineResource) (*ImageResource, error) {

// ImageResource defines an endpoint where artifacts can be stored, such as images.
type ImageResource struct {
Name string `json:"name"`
Type PipelineResourceType `json:"type"`
URL string `json:"url"`
Digest string `json:"digest"`
IndexPath string `json:"indexpath"`
Name string `json:"name"`
Type PipelineResourceType `json:"type"`
URL string `json:"url"`
Digest string `json:"digest"`
OutputImagePath string
}

// GetName returns the name of the resource
Expand All @@ -73,11 +71,10 @@ func (s ImageResource) GetParams() []Param { return []Param{} }
// Replacements is used for template replacement on an ImageResource inside of a Taskrun.
func (s *ImageResource) Replacements() map[string]string {
return map[string]string{
"name": s.Name,
"type": string(s.Type),
"url": s.URL,
"digest": s.Digest,
"indexpath": s.IndexPath,
"name": s.Name,
"type": string(s.Type),
"url": s.URL,
"digest": s.Digest,
}
}

Expand All @@ -95,9 +92,9 @@ func (s *ImageResource) GetDownloadContainerSpec() ([]corev1.Container, error) {
func (s *ImageResource) SetDestinationDirectory(path string) {
}

// GetIndexPath return the path to get the index.json file
func (s *ImageResource) GetIndexPath() string {
return s.IndexPath
// GetOutputImagePath return the path to get the index.json file
func (s *ImageResource) GetOutputImagePath() string {
return s.OutputImagePath
}

func (s ImageResource) String() string {
Expand Down
4 changes: 3 additions & 1 deletion pkg/apis/pipeline/v1alpha1/resource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ type TaskResource struct {
// +optional
// TargetPath is the path in workspace directory where the task resource will be copied.
TargetPath string `json:"targetPath"`
// Resource Value stuff
// +optional
// Path to the index.json file for output container images
OutputImagePath string `json:"outputImagePath"`
}

// +genclient
Expand Down
15 changes: 14 additions & 1 deletion pkg/apis/pipeline/v1alpha1/task_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,25 @@ limitations under the License.

package v1alpha1

import "context"
import (
"context"
"fmt"
)

func (t *Task) SetDefaults(ctx context.Context) {
t.Spec.SetDefaults(ctx)
}

// SetDefaults set any defaults for the task spec
func (ts *TaskSpec) SetDefaults(ctx context.Context) {
if ts.Outputs != nil && len(ts.Outputs.Resources) > 0 {
for i, o := range ts.Outputs.Resources {
if o.Type == PipelineResourceTypeImage {
if o.OutputImagePath == "" {
ts.Outputs.Resources[i].OutputImagePath = fmt.Sprintf("/tools/image-outputs/%s", o.Name)
}
}
}
}
return
}
5 changes: 5 additions & 0 deletions pkg/apis/pipeline/v1alpha1/task_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ func validateResourceVariables(steps []corev1.Container, inputs *Inputs, outputs
if outputs != nil {
for _, r := range outputs.Resources {
resourceNames[r.Name] = struct{}{}
if r.Type == PipelineResourceTypeImage {
if r.OutputImagePath == "" {
return apis.ErrMissingField("OutputImagePath")
}
}
}
}
return validateVariables(steps, "resources", resourceNames)
Expand Down
20 changes: 19 additions & 1 deletion pkg/apis/pipeline/v1alpha1/task_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ var validResource = TaskResource{
Type: "git",
}

var validImageResource = TaskResource{
Name: "source",
Type: "image",
}

var validBuildSteps = []corev1.Container{{
Name: "mystep",
Image: "myimage",
Expand Down Expand Up @@ -84,6 +89,17 @@ func TestTaskSpecValidate(t *testing.T) {
},
BuildSteps: validBuildSteps,
},
}, {
name: "output image resoure",
fields: fields{
Inputs: &Inputs{
Resources: []TaskResource{validImageResource},
},
Outputs: &Outputs{
Resources: []TaskResource{validImageResource},
},
BuildSteps: validBuildSteps,
},
}, {
name: "valid template variable",
fields: fields{
Expand Down Expand Up @@ -116,7 +132,9 @@ func TestTaskSpecValidate(t *testing.T) {
Outputs: tt.fields.Outputs,
Steps: tt.fields.BuildSteps,
}
if err := ts.Validate(context.Background()); err != nil {
ctx := context.Background()
ts.SetDefaults(ctx)
if err := ts.Validate(ctx); err != nil {
t.Errorf("TaskSpec.Validate() = %v", err)
}
})
Expand Down
39 changes: 31 additions & 8 deletions pkg/reconciler/v1alpha1/taskrun/resources/image_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ package resources
import (
"encoding/json"
"flag"
"os"

"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
listers "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1"
"github.com/tektoncd/pipeline/pkg/names"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
)
Expand Down Expand Up @@ -58,28 +60,49 @@ func AddOutputImageDigestExporter(
logger.Errorf("Invalid Image Resource for taskRun %q resource %v; error: %s", tr.Name, boundResource, err.Error())
return err
}
for _, o := range taskSpec.Outputs.Resources {
if o.Name == boundResource.Name {
if o.OutputImagePath != "" {
if _, err := os.Stat(o.OutputImagePath); os.IsNotExist(err) {
if err := os.MkdirAll(o.OutputImagePath, os.ModePerm); err != nil {
return err
}
}
imageResource.OutputImagePath = o.OutputImagePath
break
}
}
}
output = append(output, imageResource)
}
}

if len(output) > 0 {
augmentedSteps := []corev1.Container{}
imagesJSON, err := json.Marshal(output)
if err != nil {
return err
}

c := corev1.Container{
Name: "image-digest-exporter",
Image: *imageDigestExporterImage,
Command: []string{"/ko-app/imagedigestexporter"},
Args: []string{
"-images", string(imagesJSON),
},
for _, s := range taskSpec.Steps {
augmentedSteps = append(augmentedSteps, s)
augmentedSteps = append(augmentedSteps, imageDigestExporterContainer(s.Name, imagesJSON))
}

taskSpec.Steps = append(taskSpec.Steps, c)
taskSpec.Steps = augmentedSteps
}
}

return nil
}

func imageDigestExporterContainer(stepName string, imagesJSON []byte) corev1.Container {
return corev1.Container{
Name: names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("image-digest-exporter-" + stepName),
Image: *imageDigestExporterImage,
Command: []string{"/ko-app/imagedigestexporter"},
Args: []string{
"-images", string(imagesJSON),
},
}
}
Loading

0 comments on commit e3e3ee1

Please sign in to comment.