Skip to content

Commit

Permalink
feat: initContainers
Browse files Browse the repository at this point in the history
  • Loading branch information
joebowbeer committed Aug 1, 2023
1 parent f8dabdc commit 7ff0893
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 0 deletions.
18 changes: 18 additions & 0 deletions internal/k8s/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ func updateDeploymentContainer(d *apps_v1.Deployment, index int, image string) {
d.Spec.Template.Spec.Containers[index].Image = image
}

func updateDeploymentInitContainer(d *apps_v1.Deployment, index int, image string) {
d.Spec.Template.Spec.InitContainers[index].Image = image
}

// stateful sets https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/

func getStatefulSetIdentifier(ss *apps_v1.StatefulSet) string {
return "statefulset/" + ss.Namespace + "/" + ss.Name
}
Expand All @@ -42,6 +47,10 @@ func updateStatefulSetContainer(ss *apps_v1.StatefulSet, index int, image string
ss.Spec.Template.Spec.Containers[index].Image = image
}

func updateStatefulSetInitContainer(ss *apps_v1.StatefulSet, index int, image string) {
ss.Spec.Template.Spec.InitContainers[index].Image = image
}

// daemonsets

func getDaemonsetSetIdentifier(s *apps_v1.DaemonSet) string {
Expand All @@ -52,6 +61,10 @@ func updateDaemonsetSetContainer(s *apps_v1.DaemonSet, index int, image string)
s.Spec.Template.Spec.Containers[index].Image = image
}

func updateDaemonsetSetInitContainer(s *apps_v1.DaemonSet, index int, image string) {
s.Spec.Template.Spec.InitContainers[index].Image = image
}

// cron

func getCronJobIdentifier(s *batch_v1.CronJob) string {
Expand All @@ -61,3 +74,8 @@ func getCronJobIdentifier(s *batch_v1.CronJob) string {
func updateCronJobContainer(s *batch_v1.CronJob, index int, image string) {
s.Spec.JobTemplate.Spec.Template.Spec.Containers[index].Image = image
}

func updateCronJobInitContainer(s *batch_v1.CronJob, index int, image string) {
s.Spec.JobTemplate.Spec.Template.Spec.InitContainers[index].Image = image
}

44 changes: 44 additions & 0 deletions internal/k8s/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,21 @@ func (r *GenericResource) GetImages() (images []string) {
return
}

// GetInitImages - returns init images used by this resource
func (r *GenericResource) GetInitImages() (images []string) {
switch obj := r.obj.(type) {
case *apps_v1.Deployment:
return getContainerImages(obj.Spec.Template.Spec.InitContainers)
case *apps_v1.StatefulSet:
return getContainerImages(obj.Spec.Template.Spec.InitContainers)
case *apps_v1.DaemonSet:
return getContainerImages(obj.Spec.Template.Spec.InitContainers)
case *batch_v1.CronJob:
return getContainerImages(obj.Spec.JobTemplate.Spec.Template.Spec.InitContainers)
}
return
}

// Containers - returns containers managed by this resource
func (r *GenericResource) Containers() (containers []core_v1.Container) {
switch obj := r.obj.(type) {
Expand All @@ -290,6 +305,21 @@ func (r *GenericResource) Containers() (containers []core_v1.Container) {
return
}

// InitContainers - returns init containers managed by this resource
func (r *GenericResource) InitContainers() (containers []core_v1.Container) {
switch obj := r.obj.(type) {
case *apps_v1.Deployment:
return obj.Spec.Template.Spec.InitContainers
case *apps_v1.StatefulSet:
return obj.Spec.Template.Spec.InitContainers
case *apps_v1.DaemonSet:
return obj.Spec.Template.Spec.InitContainers
case *batch_v1.CronJob:
return obj.Spec.JobTemplate.Spec.Template.Spec.InitContainers
}
return
}

// UpdateContainer - updates container image
func (r *GenericResource) UpdateContainer(index int, image string) {
switch obj := r.obj.(type) {
Expand All @@ -304,6 +334,20 @@ func (r *GenericResource) UpdateContainer(index int, image string) {
}
}

// UpdateInitContainer - updates init container image
func (r *GenericResource) UpdateInitContainer(index int, image string) {
switch obj := r.obj.(type) {
case *apps_v1.Deployment:
updateDeploymentInitContainer(obj, index, image)
case *apps_v1.StatefulSet:
updateStatefulSetInitContainer(obj, index, image)
case *apps_v1.DaemonSet:
updateDaemonsetSetInitContainer(obj, index, image)
case *batch_v1.CronJob:
updateCronJobInitContainer(obj, index, image)
}
}

type Status struct {
// Total number of non-terminated pods targeted by this deployment (their labels match the selector).
// +optional
Expand Down
3 changes: 3 additions & 0 deletions provider/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ func (p *Provider) TrackedImages() ([]*types.TrackedImage, error) {
secrets = append(secrets, gr.GetImagePullSecrets()...)

images := gr.GetImages()
if schedule, ok := annotations[types.KeelInitContainerAnnotation]; ok && schedule == "true" {
images = append(images, gr.GetInitImages()...)
}
for _, img := range images {
ref, err := image.Parse(img)
if err != nil {
Expand Down
62 changes: 62 additions & 0 deletions provider/kubernetes/updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,68 @@ func checkForUpdate(plc policy.Policy, repo *types.Repository, resource *k8s.Gen
"policy": plc.Name(),
}).Debug("provider.kubernetes.checkVersionedDeployment: keel policy found, checking resource...")
shouldUpdateDeployment = false
if schedule, ok := resource.GetAnnotations()[types.KeelInitContainerAnnotation]; ok && schedule == "true" {
for idx, c := range resource.InitContainers() {
containerImageRef, err := image.Parse(c.Image)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image_name": c.Image,
}).Error("provider.kubernetes: failed to parse image name")
continue
}

log.WithFields(log.Fields{
"name": resource.Name,
"namespace": resource.Namespace,
"kind": resource.Kind(),
"parsed_image_name": containerImageRef.Remote(),
"target_image_name": repo.Name,
"target_tag": repo.Tag,
"policy": plc.Name(),
"image": c.Image,
}).Debug("provider.kubernetes: checking image")

if containerImageRef.Repository() != eventRepoRef.Repository() {
log.WithFields(log.Fields{
"parsed_image_name": containerImageRef.Remote(),
"target_image_name": repo.Name,
}).Debug("provider.kubernetes: images do not match, ignoring")
continue
}

shouldUpdateContainer, err := plc.ShouldUpdate(containerImageRef.Tag(), eventRepoRef.Tag())
if err != nil {
log.WithFields(log.Fields{
"error": err,
"parsed_image_name": containerImageRef.Remote(),
"target_image_name": repo.Name,
"policy": plc.Name(),
}).Error("provider.kubernetes: failed to check whether init container should be updated")
continue
}

if !shouldUpdateContainer {
continue
}

// updating spec template annotations
setUpdateTime(resource)

// updating image
if containerImageRef.Registry() == image.DefaultRegistryHostname {
resource.UpdateInitContainer(idx, fmt.Sprintf("%s:%s", containerImageRef.ShortName(), repo.Tag))
} else {
resource.UpdateInitContainer(idx, fmt.Sprintf("%s:%s", containerImageRef.Repository(), repo.Tag))
}

shouldUpdateDeployment = true

updatePlan.CurrentVersion = containerImageRef.Tag()
updatePlan.NewVersion = repo.Tag
updatePlan.Resource = resource
}
}
for idx, c := range resource.Containers() {
containerImageRef, err := image.Parse(c.Image)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const KeelMatchPreReleaseAnnotation = "keel.sh/matchPreRelease"
// KeelPollScheduleAnnotation - optional variable to setup custom schedule for polling, defaults to @every 10m
const KeelPollScheduleAnnotation = "keel.sh/pollSchedule"

// KeelInitContainerAnnotation - label or annotation to update init containers, defaults to false for backward compatibility
const KeelInitContainerAnnotation = "keel.sh/initContainers"

// KeelPollDefaultSchedule - defaul polling schedule
var KeelPollDefaultSchedule = "@every 1m"

Expand Down

0 comments on commit 7ff0893

Please sign in to comment.