Skip to content
This repository has been archived by the owner on Sep 5, 2019. It is now read-only.

Commit

Permalink
no init containers
Browse files Browse the repository at this point in the history
  • Loading branch information
aaron-prindle committed Nov 27, 2018
1 parent 5a9612f commit 2b81f15
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 149 deletions.
145 changes: 130 additions & 15 deletions pkg/reconciler/build/resources/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ import (
"io/ioutil"
"path/filepath"
"strconv"
"sync"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
v1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1"
"github.com/knative/build/pkg/credentials"
"github.com/knative/build/pkg/credentials/dockercreds"
Expand Down Expand Up @@ -258,8 +262,9 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po
var sources []v1alpha1.SourceSpec
// if source is present convert into sources

// NOTES(aaron-prindle) adds custom steps outside of user Steps for git, logs, etc
podContainers := []corev1.Container{*cred}
initContainers := []corev1.Container{*cred}
podContainers := []corev1.Container{}

if source := build.Spec.Source; source != nil {
sources = []v1alpha1.SourceSpec{*source}
}
Expand All @@ -275,26 +280,37 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po
if err != nil {
return nil, err
}
podContainers = append(podContainers, *git)
initContainers = append(initContainers, *git)
case source.GCS != nil:
gcs, err := gcsToContainer(source, i)
if err != nil {
return nil, err
}
podContainers = append(podContainers, *gcs)
initContainers = append(initContainers, *gcs)
case source.Custom != nil:
cust, err := customToContainer(source.Custom, source.Name)
if err != nil {
return nil, err
}
// Prepend the custom container to the steps, to be augmented later with env, volume mounts, etc.

build.Spec.Steps = append([]corev1.Container{*cust}, build.Spec.Steps...)
}
// webhook validation checks that only one source has subPath defined
workspaceSubPath = source.SubPath
}

// NOTES(aaron-prindle) setup volume mounts for steps
// init container that copies entrypoint binary into shared volume
// to be used by all other containers w/ entrypoint rewriting
initContainers = append(initContainers,
corev1.Container{
Name: InitContainerName,
Image: DefaultEntrypointImage,
Command: []string{"/bin/cp"},
Args: []string{"/entrypoint", BinaryLocation},
VolumeMounts: []corev1.VolumeMount{toolsMount},
})

for i, step := range build.Spec.Steps {
step.Env = append(implicitEnvVars, step.Env...)
// TODO(mattmoor): Check that volumeMounts match volumes.
Expand Down Expand Up @@ -332,6 +348,7 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po
// declared user volumes.
volumes := append(build.Spec.Volumes, implicitVolumes...)
volumes = append(volumes, secrets...)
volumes = append(volumes, toolsVolume)
if err := v1alpha1.ValidateVolumes(volumes); err != nil {
return nil, err
}
Expand All @@ -342,7 +359,7 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po
return nil, err
}
gibberish := hex.EncodeToString(b)
// entrypoint.RedirectSteps(podContainers)

RedirectSteps(podContainers)

return &corev1.Pod{
Expand Down Expand Up @@ -373,6 +390,7 @@ func MakePod(build *v1alpha1.Build, kubeclient kubernetes.Interface) (*corev1.Po
Spec: corev1.PodSpec{
// If the build fails, don't restart it.
RestartPolicy: corev1.RestartPolicyNever,
InitContainers: initContainers,
Containers: podContainers,
ServiceAccountName: build.Spec.ServiceAccountName,
Volumes: volumes,
Expand Down Expand Up @@ -497,32 +515,120 @@ const (
BinaryLocation = MountPoint + "/entrypoint"
JSONConfigEnvVar = "ENTRYPOINT_OPTIONS"
InitContainerName = "place-tools"
ProcessLogFile = "/tools/process-log.txt"
MarkerFile = "/tools/marker-file.txt"
// TODO(aaron-prindle) change this to wherever is sensible
DefaultEntrypointImage = "gcr.io/aprindle-vm-test/entrypoint:latest"

ProcessLogFile = "/tools/process-log.txt"
MarkerFile = "/tools/marker-file.txt"
ShouldWaitForPrevStep = false
PreRunFile = "0"
ShouldRunPostRun = true
PostRunFile = "0"
)

var toolsMount = corev1.VolumeMount{
Name: MountName,
MountPath: MountPoint,
}

var toolsVolume = corev1.Volume{
Name: MountName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
}

type entrypointArgs struct {
Args []string `json:"args"`
ProcessLog string `json:"process_log"`
MarkerFile string `json:"marker_file"`

ShouldWaitForPrevStep bool `json:"shouldWaitForPrevStep"`
PreRunFile string `json:"preRunFile"`
ShouldRunPostRun bool `json:"shouldRunPostRun"`
PostRunFile string `json:"postRunFile"`
}

// Cache is a simple caching mechanism allowing for caching the results of
// getting the Entrypoint of a container image from a remote registry. It
// is synchronized via a mutex so that we can share a single Cache across
// each worker thread that the reconciler is running. The mutex is necessary
// due to the possibility of a panic if two workers were to attempt to read and
// write to the internal map at the same time.
type Cache struct {
mtx sync.RWMutex
cache map[string][]string
}

// NewCache is a simple helper function that returns a pointer to a Cache that
// has had the internal cache map initialized.
func NewCache() *Cache {
return &Cache{
cache: make(map[string][]string),
}
}

func (c *Cache) get(sha string) ([]string, bool) {
c.mtx.RLock()
ep, ok := c.cache[sha]
c.mtx.RUnlock()
return ep, ok
}

func (c *Cache) set(sha string, ep []string) {
c.mtx.Lock()
c.cache[sha] = ep
c.mtx.Unlock()
}

// GetRemoteEntrypoint accepts a cache of image lookups, as well as the image
// to look for. If the cache does not contain the image, it will lookup the
// metadata from the images registry, and then commit that to the cache
func GetRemoteEntrypoint(cache *Cache, image string) ([]string, error) {
if ep, ok := cache.get(image); ok {
return ep, nil
}
// verify the image name, then download the remote config file
ref, err := name.ParseReference(image, name.WeakValidation)
if err != nil {
return nil, fmt.Errorf("couldn't parse image %s: %v", image, err)
}
img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
if err != nil {
return nil, fmt.Errorf("couldn't get container image info from registry %s: %v", image, err)
}
cfg, err := img.ConfigFile()
if err != nil {
return nil, fmt.Errorf("couldn't get config for image %s: %v", image, err)
}
cache.set(image, cfg.ContainerConfig.Entrypoint)
return cfg.ContainerConfig.Entrypoint, nil
}

// TODO(aaron-prindle) setup the cache properly
var cache = NewCache()

// RedirectSteps will modify each of the steps/containers such that
// the binary being run is no longer the one specified by the Command
// and the Args, but is instead the entrypoint binary, which will
// itself invoke the Command and Args, but also capture logs.
func RedirectSteps(steps []corev1.Container) error {
// For each step with no entrypoint set, try to populate it with the info
// from the remote registry
for i := range steps {
step := &steps[i]
e, err := getEnvVar(step.Command, step.Args)
if len(step.Command) == 0 {
ep, err := GetRemoteEntrypoint(cache, step.Image)
if err != nil {
return fmt.Errorf("could not get entrypoint from registry for %s: %v", step.Image, err)
}
step.Command = ep
}
e, err := getEnvVar(step.Command, step.Args, i)
if err != nil {
return fmt.Errorf("couldn't get env var for entrypoint: %s", err)
}

step.Command = []string{BinaryLocation}
step.Args = []string{}

Expand All @@ -535,16 +641,25 @@ func RedirectSteps(steps []corev1.Container) error {
return nil
}

func getEnvVar(cmd, args []string) (string, error) {
func getEnvVar(cmd, args []string, stepNumber int) (string, error) {
shouldWaitForPrevStep := ShouldWaitForPrevStep
// TODO(aaron-prindle) modify ShouldRunPostRun to not run on last step
if stepNumber != 0 {
shouldWaitForPrevStep = true
}

entrypointArgs := entrypointArgs{
Args: append(cmd, args...),
ProcessLog: ProcessLogFile,
MarkerFile: MarkerFile,
// TODO(aaron-prindle) add the new options here
Args: append(cmd, args...),
ProcessLog: ProcessLogFile,
MarkerFile: MarkerFile,
ShouldWaitForPrevStep: shouldWaitForPrevStep,
PreRunFile: filepath.Join(MountPoint, strconv.Itoa(stepNumber)),
ShouldRunPostRun: ShouldRunPostRun,
PostRunFile: filepath.Join(MountPoint, strconv.Itoa(stepNumber+1)),
}
j, err := json.Marshal(entrypointArgs)
if err != nil {
return "", fmt.Errorf("couldn't marshal arguments %q for entrypoint env var: %s", entrypointArgs, err)
return "", fmt.Errorf("couldn't marshal arguments %v for entrypoint env var: %s", entrypointArgs, err)
}
return string(j), nil
}
Loading

0 comments on commit 2b81f15

Please sign in to comment.