Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP/POC: handle sidecar containers #728

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions cmd/entrypoint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,24 @@ import (
)

var (
ep = flag.String("entrypoint", "", "Original specified entrypoint to execute")
waitFile = flag.String("wait_file", "", "If specified, file to wait for")
postFile = flag.String("post_file", "", "If specified, file to write upon completion")
ep = flag.String("entrypoint", "", "Original specified entrypoint to execute")
waitFile = flag.String("wait_file", "", "If specified, file to wait for")
waitFileContent = flag.Bool("wait_file_content", false, "If specified, expect wait_file to have content")
postFile = flag.String("post_file", "", "If specified, file to write upon completion")
)

func main() {
flag.Parse()

e := entrypoint.Entrypointer{
Entrypoint: *ep,
WaitFile: *waitFile,
PostFile: *postFile,
Args: flag.Args(),
Waiter: &RealWaiter{},
Runner: &RealRunner{},
PostWriter: &RealPostWriter{},
Entrypoint: *ep,
WaitFile: *waitFile,
WaitFileContent: *waitFileContent,
PostFile: *postFile,
Args: flag.Args(),
Waiter: &RealWaiter{},
Runner: &RealRunner{},
PostWriter: &RealPostWriter{},
}
if err := e.Go(); err != nil {
switch err.(type) {
Expand Down Expand Up @@ -75,14 +77,16 @@ type RealWaiter struct{}

var _ entrypoint.Waiter = (*RealWaiter)(nil)

func (*RealWaiter) Wait(file string) error {
func (*RealWaiter) Wait(file string, expectContent bool) error {
if file == "" {
return nil
}
for ; ; time.Sleep(time.Second) {
// Watch for the post file
if _, err := os.Stat(file); err == nil {
return nil
if info, err := os.Stat(file); err == nil {
if !expectContent || info.Size() > 0 {
return nil
}
} else if !os.IsNotExist(err) {
return fmt.Errorf("Waiting for %q: %v", file, err)
}
Expand Down
7 changes: 5 additions & 2 deletions pkg/entrypoint/entrypointer.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ type Entrypointer struct {
// WaitFile is the file to wait for. If not specified, execution begins
// immediately.
WaitFile string
// WaitFileContent indicates the WaitFile should have non-zero size
// before continuing with execution.
WaitFileContent bool
// PostFile is the file to write when complete. If not specified, no
// file is written.
PostFile string
Expand All @@ -45,7 +48,7 @@ type Entrypointer struct {
// Waiter encapsulates waiting for files to exist.
type Waiter interface {
// Wait blocks until the specified file exists.
Wait(file string) error
Wait(file string, expectContent bool) error
}

// Runner encapsulates running commands.
Expand All @@ -63,7 +66,7 @@ type PostWriter interface {
// post file.
func (e Entrypointer) Go() error {
if e.WaitFile != "" {
if err := e.Waiter.Wait(e.WaitFile); err != nil {
if err := e.Waiter.Wait(e.WaitFile, e.WaitFileContent); err != nil {
// An error happened while waiting, so we bail
// *but* we write postfile to make next steps bail too
e.WritePostFile(e.PostFile, err)
Expand Down
48 changes: 33 additions & 15 deletions pkg/reconciler/v1alpha1/taskrun/entrypoint/entrypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,25 @@ import (
const (
// MountName is the name of the pvc being mounted (which
// will contain the entrypoint binary and eventually the logs)
MountName = "tools"
MountPoint = "/builder/tools"
BinaryLocation = MountPoint + "/entrypoint"
JSONConfigEnvVar = "ENTRYPOINT_OPTIONS"
InitContainerName = "place-tools"
cacheSize = 1024
MountName = "tools"
MountPoint = "/builder/tools"
DownwardMountName = "downward"
DownwardMountPoint = "/builder/downward"
DownwardMountReadyFile = "ready"
BinaryLocation = MountPoint + "/entrypoint"
JSONConfigEnvVar = "ENTRYPOINT_OPTIONS"
InitContainerName = "place-tools"
cacheSize = 1024
)

var toolsMount = corev1.VolumeMount{
Name: MountName,
MountPath: MountPoint,
}
var downwardMount = corev1.VolumeMount{
Name: DownwardMountName,
MountPath: DownwardMountPoint,
}
var (
entrypointImage = flag.String("entrypoint-image", "override-with-entrypoint:latest",
"The container image containing our entrypoint binary.")
Expand Down Expand Up @@ -134,34 +141,45 @@ func RedirectStep(cache *Cache, stepNum int, step *corev1.Container, kubeclient
step.Args = GetArgs(stepNum, step.Command, step.Args)
step.Command = []string{BinaryLocation}
step.VolumeMounts = append(step.VolumeMounts, toolsMount)
if stepNum == 0 {
step.VolumeMounts = append(step.VolumeMounts, downwardMount)
}
return nil
}

// GetArgs returns the arguments that should be specified for the step which has been wrapped
// such that it will execute our custom entrypoint instead of the user provided Command and Args.
func GetArgs(stepNum int, commands, args []string) []string {
waitFile := fmt.Sprintf("%s/%s", MountPoint, strconv.Itoa(stepNum-1))
if stepNum == 0 {
waitFile = ""
}
waitFile := getWaitFile(stepNum)
// The binary we want to run must be separated from its arguments by --
// so if commands has more than one value, we'll move the other values
// into the arg list so we can separate them
if len(commands) > 1 {
args = append(commands[1:], args...)
commands = commands[:1]
}
argsForEntrypoint := append([]string{
argsForEntrypoint := []string{
"-wait_file", waitFile,
"-post_file", fmt.Sprintf("%s/%s", MountPoint, strconv.Itoa(stepNum)),
"-entrypoint"},
commands...,
)
"-post_file", getWaitFile(stepNum + 1),
}
if stepNum == 0 {
argsForEntrypoint = append(argsForEntrypoint, "-wait_file_content")
}
argsForEntrypoint = append(argsForEntrypoint, "-entrypoint")
argsForEntrypoint = append(argsForEntrypoint, commands...)
// TODO: what if Command has multiple elements, do we need "--" between command and args?
argsForEntrypoint = append(argsForEntrypoint, "--")
return append(argsForEntrypoint, args...)
}

func getWaitFile(stepNum int) string {
if stepNum == 0 {
return fmt.Sprintf("%s/%s", DownwardMountPoint, DownwardMountReadyFile)
}

return fmt.Sprintf("%s/%s", MountPoint, strconv.Itoa(stepNum-1))
}

// GetRemoteEntrypoint accepts a cache of digest lookups, as well as the digest
// to look for. If the cache does not contain the digest, it will lookup the
// metadata from the images registry, and then commit that to the cache
Expand Down
34 changes: 23 additions & 11 deletions pkg/reconciler/v1alpha1/taskrun/resources/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,15 @@ const (
// Name of the credential initialization container.
credsInit = "credential-initializer"
// Name of the working dir initialization container.
workingDirInit = "working-dir-initializer"
workingDirInit = "working-dir-initializer"
ReadyAnnotation = "tekton.dev/ready"
readyAnnotationValue = "READY"
)

var (
// The container used to initialize credentials before the build runs.
credsImage = flag.String("creds-image", "override-with-creds:latest",
"The container image for preparing our Build's credentials.")
// The container that just prints build successful.
nopImage = flag.String("nop-image", "override-with-nop:latest",
"The container image run at the end of the build to log build success")
)

func makeCredentialInitializer(serviceAccountName, namespace string, kubeclient kubernetes.Interface) (*corev1.Container, []corev1.Volume, error) {
Expand Down Expand Up @@ -237,7 +236,7 @@ func MakePod(taskRun *v1alpha1.TaskRun, taskSpec v1alpha1.TaskSpec, kubeclient k
for key, val := range taskRun.Annotations {
annotations[key] = val
}
annotations["sidecar.istio.io/inject"] = "false"
annotations[ReadyAnnotation] = ""

cred, secrets, err := makeCredentialInitializer(taskRun.Spec.ServiceAccount, taskRun.Namespace, kubeclient)
if err != nil {
Expand Down Expand Up @@ -300,12 +299,6 @@ func MakePod(taskRun *v1alpha1.TaskRun, taskSpec v1alpha1.TaskSpec, kubeclient k
}
gibberish := hex.EncodeToString(b)

nopContainer := &corev1.Container{Name: "nop", Image: *nopImage, Command: []string{"/ko-app/nop"}}
if err := entrypoint.RedirectStep(cache, len(podContainers), nopContainer, kubeclient, taskRun, logger); err != nil {
return nil, err
}
podContainers = append(podContainers, *nopContainer)

mergedInitContainers, err := merge.CombineStepsWithContainerTemplate(taskSpec.ContainerTemplate, initContainers)
if err != nil {
return nil, err
Expand Down Expand Up @@ -345,6 +338,25 @@ func MakePod(taskRun *v1alpha1.TaskRun, taskSpec v1alpha1.TaskSpec, kubeclient k
}, nil
}

type UpdatePod func(*corev1.Pod) (*corev1.Pod, error)

// AddReadyAnnotation adds the ready annotation if it is not present.
// Returns true if the pod needs updating
func AddReadyAnnotation(p *corev1.Pod, update UpdatePod) error {
if p.ObjectMeta.Annotations[ReadyAnnotation] != readyAnnotationValue {
p.ObjectMeta.Annotations[ReadyAnnotation] = readyAnnotationValue
_, err := update(p)

return err
}

return nil
}

func IsContainerStep(name string) bool {
return strings.HasPrefix(name, containerPrefix)
}

// makeLabels constructs the labels we will propagate from TaskRuns to Pods.
func makeLabels(s *v1alpha1.TaskRun) map[string]string {
labels := make(map[string]string, len(s.ObjectMeta.Labels)+1)
Expand Down
Loading