Skip to content

Commit

Permalink
enhancements for the exporting output image digest
Browse files Browse the repository at this point in the history
 - use pod termination message to export the image digest instead of logs
 - add more logs when index.json is not found
 - create a container to create the default folder for image output digest dir
  • Loading branch information
nader-ziada committed Jun 29, 2019
1 parent 60ce931 commit 5e01e74
Show file tree
Hide file tree
Showing 12 changed files with 341 additions and 15 deletions.
21 changes: 17 additions & 4 deletions cmd/imagedigestexporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"

"github.com/google/go-containerregistry/pkg/v1/layout"
v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
Expand All @@ -41,8 +41,7 @@ func main() {
flag.Parse()

imageResources := []*v1alpha1.ImageResource{}
err := json.Unmarshal([]byte(*images), &imageResources)
if err != nil {
if err := json.Unmarshal([]byte(*images), &imageResources); err != nil {
log.Fatalf("Error reading images array: %v", err)
}

Expand All @@ -52,6 +51,7 @@ func main() {
if err != nil {
// if this image doesn't have a builder that supports index.json file,
// then it will be skipped
log.Printf("ImageResource %s doesn't have an index.json file: %s", imageResource.Name, err)
continue
}
digest, err := ii.Digest()
Expand All @@ -65,5 +65,18 @@ func main() {
if err != nil {
log.Fatalf("Unexpected error converting images to json %v: %v", output, err)
}
fmt.Println(string(imagesJSON))
log.Printf("Image digest exporter output: %s ", string(imagesJSON))
f, err := os.OpenFile("/workspace/builder/termination-log", os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
log.Fatalf("Unexpected error converting images to json %v: %v", output, err)
}
defer f.Close()

_, err = f.Write(imagesJSON)
if err != nil {
log.Fatalf("Unexpected error converting images to json %v: %v", output, err)
}
if err := f.Sync(); err != nil {
log.Fatalf("Unexpected error converting images to json %v: %v", output, err)
}
}
2 changes: 1 addition & 1 deletion docs/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ spec:
```

If no value is specified for `outputImageDir`, it will default to
`/builder/image-outputs/{resource-name}`.
`/builder/home/image-outputs/{resource-name}`.

_Please check the builder tool used on how to pass this path to create the
output file._
Expand Down
128 changes: 128 additions & 0 deletions examples/taskruns/task-multiple-output-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: skaffold-image-leeroy-web-1
spec:
type: image
params:
- name: url
value: gcr.io/christiewilson-catfactory/leeroy-web # Replace this URL with ${KO_DOCKER_REPO}
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: skaffold-image-leeroy-web-2
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: multiple-build-push-kaniko
spec:
inputs:
resources:
- name: sourcerepo
type: git
outputs:
resources:
- name: builtImage1
type: image
- name: builtImage2
type: image
steps:
- name: build-and-push-1
image: busybox
command:
- /bin/sh
args:
- -ce
- |
set -ex
cat <<EOF > /builder/home/image-outputs/builtImage1/index.json
{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"size": 314,
"digest": "sha256:05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5"
}
]
}
EOF
- name: build-and-push-2
image: busybox
command:
- /bin/sh
args:
- -ce
- |
set -e
cat <<EOF > /builder/home/image-outputs/builtImage2/index.json
{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"size": 314,
"digest": "sha256:05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5"
}
]
}
EOF
---
apiVersion: tekton.dev/v1alpha1
kind: TaskRun
metadata:
name: multiple-build-push-kaniko-run
spec:
taskRef:
name: multiple-build-push-kaniko
trigger:
type: manual
inputs:
resources:
- name: sourcerepo
resourceRef:
name: skaffold-git
outputs:
resources:
- name: builtImage1
resourceRef:
name: skaffold-image-leeroy-web-1
- name: builtImage2
resourceRef:
name: skaffold-image-leeroy-web-2
2 changes: 1 addition & 1 deletion pkg/apis/pipeline/v1alpha1/task_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (ts *TaskSpec) SetDefaults(ctx context.Context) {
for i, o := range ts.Outputs.Resources {
if o.Type == PipelineResourceTypeImage {
if o.OutputImageDir == "" {
ts.Outputs.Resources[i].OutputImageDir = fmt.Sprintf("/builder/image-outputs/%s", o.Name)
ts.Outputs.Resources[i].OutputImageDir = fmt.Sprintf("%s/%s", TaskOutputImageDefaultDir, o.Name)
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/pipeline/v1alpha1/task_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ type TaskSpec struct {
var _ apis.Validatable = (*Task)(nil)
var _ apis.Defaultable = (*Task)(nil)

const (
// TaskOutputImageDefaultDir is the default directory for output image resource,
TaskOutputImageDefaultDir = "/builder/home/image-outputs"
)

// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down
5 changes: 4 additions & 1 deletion pkg/reconciler/v1alpha1/taskrun/resources/image_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,13 @@ func AddOutputImageDigestExporter(

taskSpec.Steps = augmentedSteps
}

}

return nil
}

// UpdateTaskRunStatusWithResourceResult if there an update to the outout image resource, add to taskrun status result
// UpdateTaskRunStatusWithResourceResult if there is an update to the outout image resource, add to taskrun status result
func UpdateTaskRunStatusWithResourceResult(taskRun *v1alpha1.TaskRun, logContent []byte) error {
err := json.Unmarshal(logContent, &taskRun.Status.ResourcesResult)
if err != nil {
Expand All @@ -102,6 +103,8 @@ func imageDigestExporterContainer(stepName string, imagesJSON []byte) corev1.Con
Args: []string{
"-images", string(imagesJSON),
},
TerminationMessagePath: "/workspace/builder/termination-log",
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ func TestAddOutputImageDigestExporter(t *testing.T) {
Image: "override-with-imagedigest-exporter-image:latest",
Command: []string{"/ko-app/imagedigestexporter"},
Args: []string{"-images", fmt.Sprintf("[{\"name\":\"source-image-1\",\"type\":\"image\",\"url\":\"gcr.io/some-image-1\",\"digest\":\"\",\"OutputImageDir\":\"%s\"}]", currentDir)},
TerminationMessagePath: "/workspace/builder/termination-log",
TerminationMessagePolicy: "FallbackToLogsOnError",
}},
}, {
desc: "image resource in task with multiple steps",
Expand Down Expand Up @@ -155,13 +157,17 @@ func TestAddOutputImageDigestExporter(t *testing.T) {
Image: "override-with-imagedigest-exporter-image:latest",
Command: []string{"/ko-app/imagedigestexporter"},
Args: []string{"-images", fmt.Sprintf("[{\"name\":\"source-image-1\",\"type\":\"image\",\"url\":\"gcr.io/some-image-1\",\"digest\":\"\",\"OutputImageDir\":\"%s\"}]", currentDir)},
TerminationMessagePath: "/workspace/builder/termination-log",
TerminationMessagePolicy: "FallbackToLogsOnError",
}, {
Name: "step2",
}, {
Name: "image-digest-exporter-step2-mz4c7",
Image: "override-with-imagedigest-exporter-image:latest",
Command: []string{"/ko-app/imagedigestexporter"},
Args: []string{"-images", fmt.Sprintf("[{\"name\":\"source-image-1\",\"type\":\"image\",\"url\":\"gcr.io/some-image-1\",\"digest\":\"\",\"OutputImageDir\":\"%s\"}]", currentDir)},
TerminationMessagePath: "/workspace/builder/termination-log",
TerminationMessagePolicy: "FallbackToLogsOnError",
},
},
}} {
Expand Down
22 changes: 22 additions & 0 deletions pkg/reconciler/v1alpha1/taskrun/resources/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,26 @@ func makeWorkingDirInitializer(steps []corev1.Container) *corev1.Container {
return nil
}

// initOutputResourcesDefaultDir checks if there are any output image resources expecting a default path
// and creates an init container to create that folder
func initOutputResourcesDefaultDir(taskRun *v1alpha1.TaskRun, taskSpec v1alpha1.TaskSpec) []corev1.Container {
makeDirSteps := []corev1.Container{}
if len(taskRun.Spec.Outputs.Resources) > 0 {
for _, r := range taskRun.Spec.Outputs.Resources {
for _, o := range taskSpec.Outputs.Resources {
if o.Name == r.Name {
if strings.HasPrefix(o.OutputImageDir, v1alpha1.TaskOutputImageDefaultDir) {
cn := v1alpha1.CreateDirContainer("default-image-output", fmt.Sprintf("%s/%s", v1alpha1.TaskOutputImageDefaultDir, r.Name))
cn.VolumeMounts = append(cn.VolumeMounts, implicitVolumeMounts...)
makeDirSteps = append(makeDirSteps, cn)
}
}
}
}
}
return makeDirSteps
}

// GetPod returns the Pod for the given pod name
type GetPod func(string, metav1.GetOptions) (*corev1.Pod, error)

Expand Down Expand Up @@ -239,6 +259,8 @@ func MakePod(taskRun *v1alpha1.TaskRun, taskSpec v1alpha1.TaskSpec, kubeclient k
initContainers = append(initContainers, *workingDir)
}

initContainers = append(initContainers, initOutputResourcesDefaultDir(taskRun, taskSpec)...)

maxIndicesByResource := findMaxResourceRequest(taskSpec.Steps, corev1.ResourceCPU, corev1.ResourceMemory, corev1.ResourceEphemeralStorage)

for i := range taskSpec.Steps {
Expand Down
Loading

0 comments on commit 5e01e74

Please sign in to comment.