Skip to content

Commit

Permalink
Add image registry cache
Browse files Browse the repository at this point in the history
* Split up image cache configuration
* Split up build cache configuration
* Add registy/image based cache
* Remove unused buildCacheName arg
* Update documentation
* Add basic registry-cache e2e test
* Fix duplicate import
* Fix unmatched quotes
* Add home volume to restore step
* Allow build.cache to be nil
* Persist cache image in build
* Use cache image digest if present
* Move changes to buildapi from v1alpha1 to v1alpha2

Co-authored-by: Ralf Pannemans <ralf.pannemans@sap.com>
Co-authored-by: Johannes Dillmann <j.dillmann@sap.com>
Co-authored-by: Sumit Kulhadia <sumit.kulhadia@sap.com>
  • Loading branch information
3 people committed Aug 20, 2021
1 parent 33c4d25 commit 590aba5
Show file tree
Hide file tree
Showing 23 changed files with 586 additions and 134 deletions.
9 changes: 9 additions & 0 deletions build-webhook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

set -e

pack build europe-west3-docker.pkg.dev/sap-se-gcp-istio-dev/public/kpack-667/webhook --builder gcr.io/cf-build-service-public/ci/tiny-builder -e BP_GO_TARGETS=./cmd/controller --publish --trust-builder
docker pull europe-west3-docker.pkg.dev/sap-se-gcp-istio-dev/public/kpack-667/webhook
kubectl -n kpack set image deploy/kpack-webhook webhook=`docker inspect --format='{{index .RepoDigests 0}}' europe-west3-docker.pkg.dev/sap-se-gcp-istio-dev/public/kpack-667/webhook`


9 changes: 9 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

set -e

pack build europe-west3-docker.pkg.dev/sap-se-gcp-istio-dev/public/kpack-667/controller --builder gcr.io/cf-build-service-public/ci/tiny-builder -e BP_GO_TARGETS=./cmd/controller --publish --trust-builder
docker pull europe-west3-docker.pkg.dev/sap-se-gcp-istio-dev/public/kpack-667/controller
kubectl -n kpack set image deploy/kpack-controller controller=`docker inspect --format='{{index .RepoDigests 0}}' europe-west3-docker.pkg.dev/sap-se-gcp-istio-dev/public/kpack-667/controller`


7 changes: 5 additions & 2 deletions docs/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ spec:
image: gcr.io/paketo-buildpacks/builder:base
imagePullSecrets:
- name: builder-secret
cacheName: persisent-volume-claim-name
cache:
volumeName: persisent-volume-claim-name
projectDescriptorPath: path/to/project.toml
source:
git:
Expand All @@ -42,7 +43,9 @@ spec:
- `builder.image`: This is the tag to the [Cloud Native Buildpacks builder image](https://buildpacks.io/docs/using-pack/working-with-builders/) to use in the build. Unlike on the Image resource, this is an image not a reference to a Builder resource.
- `builder.imagePullSecrets`: An optional list of pull secrets if the builder is in a private registry. [To create this secret please reference this link](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials)
- `source`: The source location that will be the input to the build. See the [Source Configuration](#source-config) section below.
- `cacheName`: Optional name of a persistent volume claim to used for a build cache across builds.
- `cache`: Caching configuration, two variants are available:
- `volumeName`: Optional name of a persistent volume claim used for a build cache across builds.
- `imageTag`: Optional name of a tag used for a build cache across builds.
- `env`: Optional list of build time environment variables.
- `projectDescriptorPath`: Path to the [project descriptor file](https://buildpacks.io/docs/reference/config/project-descriptor/) relative to source root dir or `subPath` if set. If unset, kpack will look for `project.toml` at the root dir or `subPath` if set.
- `resources`: Optional configurable resource limits on `CPU` and `memory`.
Expand Down
12 changes: 9 additions & 3 deletions docs/image.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ The following defines the relevant fields of the `image` resource spec in more d
- `builder`: Configuration of the `builder` resource the image builds will use. See more info [Builder Configuration](builders.md).
- `serviceAccount`: The Service Account name that will be used for credential lookup.
- `source`: The source code that will be monitored/built into images. See the [Source Configuration](#source-config) section below.
- `cacheSize`: The size of the Volume Claim that will be used by the build cache.
- `cache`: Caching configuration, two variants are available:
- `volume.request`: Creates a Volume Claim of the given size
- `registry.tag`: Creates an image with cached contents
- `failedBuildHistoryLimit`: The maximum number of failed builds for an image that will be retained.
- `successBuildHistoryLimit`: The maximum number of successful builds for an image that will be retained.
- `imageTaggingStrategy`: Allow for builds to be additionally tagged with the build number. Valid options are `None` and `BuildNumber`.
Expand Down Expand Up @@ -153,7 +155,9 @@ spec:
builder:
name: sample-builder
kind: ClusterBuilder
cacheSize: "1.5Gi" # Optional, if not set then the caching feature is disabled
cache:
volume:
request: "1.5Gi" # Optional, if not set then the caching feature is disabled
failedBuildHistoryLimit: 5 # Optional, if not present defaults to 10
successBuildHistoryLimit: 5 # Optional, if not present defaults to 10
source:
Expand Down Expand Up @@ -196,7 +200,9 @@ spec:
builder:
name: sample-builder
kind: ClusterBuilder
cacheSize: "1.5Gi" # Optional, if not set then the caching feature is disabled
cache:
volume:
request: "1.5Gi" # Optional, if not set then the caching feature is disabled
failedBuildHistoryLimit: 5 # Optional, if not present defaults to 10
successBuildHistoryLimit: 5 # Optional, if not present defaults to 10
source:
Expand Down
11 changes: 11 additions & 0 deletions pkg/apis/build/v1alpha2/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ func (b *Build) BuiltImage() string {
return b.Status.LatestImage
}

func (b *Build) CacheImage() string {
if b == nil {
return ""
}
if !b.IsSuccess() {
return ""
}

return b.Status.LatestCacheImage
}

func (b *Build) IsSuccess() bool {
if b == nil {
return false
Expand Down
23 changes: 17 additions & 6 deletions pkg/apis/build/v1alpha2/build_pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/pkg/kmeta"
)

Expand Down Expand Up @@ -164,17 +163,28 @@ func (b *Build) BuildPod(images BuildPodImages, secrets []corev1.Secret, taints
}

var cacheArgs []string
var exportCacheArgs []string
var cacheVolumes []corev1.VolumeMount
if b.Spec.CacheName == "" || config.OS == "windows" {

if (b.Spec.Cache.ImageTag == "" && b.Spec.Cache.VolumeName == "") || config.OS == "windows" {
cacheArgs = nil
cacheVolumes = nil
} else if len(b.Spec.Cache.ImageTag) > 0 {
if b.Spec.LastBuild != nil && b.Spec.LastBuild.CacheImage != "" {
cacheArgs = []string{fmt.Sprintf("-cache-image=%s", b.Spec.LastBuild.CacheImage)}
} else {
cacheArgs = []string{fmt.Sprintf("-cache-image=%s", b.Spec.Cache.ImageTag)}
}
exportCacheArgs = []string{fmt.Sprintf("-cache-image=%s", b.Spec.Cache.ImageTag)}
cacheVolumes = nil
} else {
cacheArgs = []string{"-cache-dir=/cache"}
exportCacheArgs = cacheArgs
cacheVolumes = []corev1.VolumeMount{cacheVolume}
}

return &corev1.Pod{
ObjectMeta: v1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: b.PodName(),
Namespace: b.Namespace,
Labels: combine(b.Labels, map[string]string{
Expand Down Expand Up @@ -344,6 +354,7 @@ func (b *Build) BuildPod(images BuildPodImages, secrets []corev1.Secret, taints
}, cacheArgs),
VolumeMounts: append([]corev1.VolumeMount{
layersVolume,
homeVolume,
}, cacheVolumes...),
Env: []corev1.EnvVar{
{
Expand Down Expand Up @@ -392,7 +403,7 @@ func (b *Build) BuildPod(images BuildPodImages, secrets []corev1.Secret, taints
"-group=/layers/group.toml",
"-analyzed=/layers/analyzed.toml",
"-project-metadata=/layers/project-metadata.toml"},
cacheArgs,
exportCacheArgs,
func() []string {
if platformAPI == "0.3" {
return nil
Expand Down Expand Up @@ -645,14 +656,14 @@ func (b *Build) rebasePod(secrets []corev1.Secret, images BuildPodImages, config
}

func (b *Build) cacheVolume(os string) []corev1.Volume {
if b.Spec.CacheName == "" || os == "windows" {
if b.Spec.Cache.VolumeName == "" || os == "windows" {
return []corev1.Volume{}
}

return []corev1.Volume{{
Name: cacheDirName,
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: b.Spec.CacheName},
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: b.Spec.Cache.VolumeName},
},
}}
}
Expand Down
72 changes: 69 additions & 3 deletions pkg/apis/build/v1alpha2/build_pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ func testBuildPod(t *testing.T, when spec.G, it spec.S) {
Revision: "gitrev1234",
},
},
CacheName: "some-cache-name",
Cache: &buildapi.BuildCacheConfig{
VolumeName: "some-cache-name",
},
Bindings: []buildapi.Binding{
{
Name: "database",
Expand Down Expand Up @@ -581,6 +583,7 @@ func testBuildPod(t *testing.T, when spec.G, it spec.S) {
assert.Equal(t, pod.Spec.InitContainers[3].Image, builderImage)
assert.Equal(t, []string{
"layers-dir",
"home-dir",
"cache-dir",
}, names(pod.Spec.InitContainers[3].VolumeMounts))

Expand Down Expand Up @@ -668,9 +671,72 @@ func testBuildPod(t *testing.T, when spec.G, it spec.S) {
}, pod.Spec.Volumes[0])
})

when("registry cache is requested", func() {
podWithVolumeCache, _ := build.BuildPod(config, nil, nil, buildPodBuilderConfig)
build.Spec.Cache.VolumeName = ""
build.Spec.Cache.ImageTag = "test-cache-image"

it("creates a pod without cache volume", func() {
podWithImageCache, err := build.BuildPod(config, nil, nil, buildPodBuilderConfig)
require.NoError(t, err)

assert.Len(t, podWithImageCache.Spec.Volumes, len(podWithVolumeCache.Spec.Volumes)-1)
})

it("adds the cache to analyze container", func() {
podWithImageCache, err := build.BuildPod(config, nil, nil, buildPodBuilderConfig)
require.NoError(t, err)

analyzeContainer := podWithImageCache.Spec.InitContainers[2]
assert.Contains(t, analyzeContainer.Args, "-cache-image=test-cache-image")
})
it("adds the cache to restore container", func() {
podWithImageCache, err := build.BuildPod(config, nil, nil, buildPodBuilderConfig)
require.NoError(t, err)

restoreContainer := podWithImageCache.Spec.InitContainers[3]
assert.Contains(t, restoreContainer.Args, "-cache-image=test-cache-image")
})
it("adds the cache to export container", func() {
podWithImageCache, err := build.BuildPod(config, nil, nil, buildPodBuilderConfig)
require.NoError(t, err)

exportContainer := podWithImageCache.Spec.InitContainers[5]
assert.Contains(t, exportContainer.Args, "-cache-image=test-cache-image")
})
})

when("Tag is empty", func() {
var pod *corev1.Pod
var err error
build.Spec.Cache.ImageTag = ""

it("does not add the cache to analyze container", func() {
pod, err = build.BuildPod(config, nil, nil, buildPodBuilderConfig)
require.NoError(t, err)

analyzeContainer := pod.Spec.InitContainers[2]
assert.NotContains(t, analyzeContainer.Args, "-cache-image")
})
it("does not add the cache to restore container", func() {
pod, err = build.BuildPod(config, nil, nil, buildPodBuilderConfig)
require.NoError(t, err)

restoreContainer := pod.Spec.InitContainers[3]
assert.NotContains(t, restoreContainer.Args, "-cache-image")
})
it("does not add the cache to export container", func() {
pod, err = build.BuildPod(config, nil, nil, buildPodBuilderConfig)
require.NoError(t, err)

exportContainer := pod.Spec.InitContainers[5]
assert.NotContains(t, exportContainer.Args, "-cache-image")
})
})

when("CacheName is empty", func() {
podWithCache, _ := build.BuildPod(config, nil, nil, buildPodBuilderConfig)
build.Spec.CacheName = ""
build.Spec.Cache.VolumeName = ""

it("creates a pod without cache volume", func() {
pod, err := build.BuildPod(config, nil, nil, buildPodBuilderConfig)
Expand Down Expand Up @@ -1285,7 +1351,7 @@ func testBuildPod(t *testing.T, when spec.G, it spec.S) {
buildPodBuilderConfigLinux := buildPodBuilderConfig.DeepCopy()
buildPodBuilderConfigLinux.OS = "linux"
podWithCache, _ := build.BuildPod(config, nil, nil, *buildPodBuilderConfigLinux)
build.Spec.CacheName = "non-empty"
build.Spec.Cache.VolumeName = "non-empty"

pod, err := build.BuildPod(config, nil, nil, buildPodBuilderConfig)
require.NoError(t, err)
Expand Down
23 changes: 15 additions & 8 deletions pkg/apis/build/v1alpha2/build_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ type BuildBuilderSpec struct {
// +k8s:openapi-gen=true
type BuildSpec struct {
// +listType
Tags []string `json:"tags,omitempty"`
Builder BuildBuilderSpec `json:"builder,omitempty"`
ServiceAccount string `json:"serviceAccount,omitempty"`
Source SourceConfig `json:"source"`
CacheName string `json:"cacheName,omitempty"`
Tags []string `json:"tags,omitempty"`
Builder BuildBuilderSpec `json:"builder,omitempty"`
ServiceAccount string `json:"serviceAccount,omitempty"`
Source SourceConfig `json:"source"`
Cache *BuildCacheConfig `json:"cache,omitempty"`
// +listType
Bindings Bindings `json:"bindings,omitempty"`
// +listType
Expand All @@ -70,20 +70,26 @@ type BuildSpec struct {
Notary *NotaryConfig `json:"notary,omitempty"`
}

type BuildCacheConfig struct {
VolumeName string `json:"volumeName,omitempty"`
ImageTag string `json:"imageTag,omitempty"`
}

// +k8s:openapi-gen=true
type Bindings []Binding

// +k8s:openapi-gen=true
type Binding struct {
Name string `json:"name",omitempty"`
Name string `json:"name,omitempty"`
MetadataRef *corev1.LocalObjectReference `json:"metadataRef,omitempty"`
SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"`
}

// +k8s:openapi-gen=true
type LastBuild struct {
Image string `json:"image,omitempty"`
StackId string `json:"stackId,omitempty"`
Image string `json:"image,omitempty"`
CacheImage string `json:"cacheImage,omitempty"`
StackId string `json:"stackId,omitempty"`
}

// +k8s:openapi-gen=true
Expand All @@ -98,6 +104,7 @@ type BuildStatus struct {
BuildMetadata BuildpackMetadataList `json:"buildMetadata,omitempty"`
Stack BuildStack `json:"stack,omitempty"`
LatestImage string `json:"latestImage,omitempty"`
LatestCacheImage string `json:"latestCacheImage,omitempty"`
PodName string `json:"podName,omitempty"`
// +listType
StepStates []corev1.ContainerState `json:"stepStates,omitempty"`
Expand Down
Loading

0 comments on commit 590aba5

Please sign in to comment.