Skip to content

Commit

Permalink
Add handler for init containers (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
bitsofdave authored Apr 2, 2018
1 parent e231879 commit 54f975d
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 99 deletions.
1 change: 1 addition & 0 deletions pipeline/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ func (b *Builder) buildDeployStage(index int, s config.Stage) (*types.DeployStag
Account: s.Account,
Application: b.pipeline.Application,
Containers: mg.Containers,
InitContainers: mg.InitContainers,
LoadBalancers: group.LoadBalancers,
Region: mg.Namespace,
Namespace: mg.Namespace,
Expand Down
215 changes: 116 additions & 99 deletions pipeline/builder/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type ManifestGroup struct {
Annotations map[string]string
PodAnnotations map[string]string
Containers []*types.Container
InitContainers []*types.Container
VolumeSources []*types.VolumeSource
}

Expand Down Expand Up @@ -92,12 +93,14 @@ func (mp *ManifestParser) ContainersFromScaffold(scaffold config.ContainerScaffo
switch t := resource.(type) {
case *appsv1.Deployment:
mg.Containers = mp.deploymentContainers(t.Spec.Template.Spec, scaffold)
mg.InitContainers = mp.deploymentInitContainers(t.Spec.Template.Spec, scaffold)
mg.Annotations = t.Annotations
mg.PodAnnotations = t.Spec.Template.Annotations
mg.Namespace = t.GetNamespace()
mg.VolumeSources = mp.volumeSources(t.Spec.Template.Spec.Volumes)
case *corev1.Pod:
mg.Containers = mp.deploymentContainers(t.Spec, scaffold)
mg.InitContainers = mp.deploymentInitContainers(t.Spec, scaffold)
mg.Annotations = t.Annotations
mg.PodAnnotations = t.Annotations
mg.Namespace = t.GetNamespace()
Expand Down Expand Up @@ -153,128 +156,142 @@ func (mp *ManifestParser) deploymentContainers(podspec corev1.PodSpec, scaffold
var c []*types.Container

for _, container := range podspec.Containers {
spinContainer := &types.Container{}

// add the image description first off using the annotations on the container
var imageDescription config.ImageDescription
if ref := scaffold.ImageDescriptionRef(container.Name); ref != nil {
for _, desc := range mp.config.ImageDescriptions {
if desc.Name == ref.Name && ref.ContainerName == container.Name {
imageDescription = desc
break
}
c = append(c, mp.parseContainer(container, scaffold))
}

return c
}

func (mp *ManifestParser) deploymentInitContainers(podspec corev1.PodSpec, scaffold config.ContainerScaffold) []*types.Container {
var c []*types.Container

for _, container := range podspec.InitContainers {
c = append(c, mp.parseContainer(container, scaffold))
}

return c
}

func (mp *ManifestParser) parseContainer(container corev1.Container, scaffold config.ContainerScaffold) *types.Container {
spinContainer := &types.Container{}

// add the image description first off using the annotations on the container
var imageDescription config.ImageDescription
if ref := scaffold.ImageDescriptionRef(container.Name); ref != nil {
for _, desc := range mp.config.ImageDescriptions {
if desc.Name == ref.Name && ref.ContainerName == container.Name {
imageDescription = desc
break
}
}
spinContainer.ImageDescription = types.ImageDescription{
Account: imageDescription.Account,
ImageID: imageDescription.ImageID,
Tag: imageDescription.Tag,
Repository: imageDescription.Repository,
Registry: imageDescription.Registry,
Organization: imageDescription.Organization,
}
}
spinContainer.ImageDescription = types.ImageDescription{
Account: imageDescription.Account,
ImageID: imageDescription.ImageID,
Tag: imageDescription.Tag,
Repository: imageDescription.Repository,
Registry: imageDescription.Registry,
Organization: imageDescription.Organization,
}

args := []string{}
if container.Args != nil {
args = container.Args
}
args := []string{}
if container.Args != nil {
args = container.Args
}

spinContainer.Name = container.Name
spinContainer.Args = args
spinContainer.Command = container.Command
spinContainer.ImagePullPolicy = strings.ToUpper(string(container.ImagePullPolicy))
spinContainer.Requests.CPU = container.Resources.Requests.Cpu().String()
spinContainer.Requests.Memory = container.Resources.Requests.Memory().String()
spinContainer.Limits.CPU = container.Resources.Limits.Cpu().String()
spinContainer.Limits.Memory = container.Resources.Limits.Memory().String()

// appends all of the ports on the deployment type into the spinnaker definition
for _, port := range container.Ports {
spinContainer.Ports = append(spinContainer.Ports, types.Port{
ContainerPort: port.ContainerPort,
Name: port.Name,
Protocol: string(port.Protocol),
})
}
spinContainer.Name = container.Name
spinContainer.Args = args
spinContainer.Command = container.Command
spinContainer.ImagePullPolicy = strings.ToUpper(string(container.ImagePullPolicy))
spinContainer.Requests.CPU = container.Resources.Requests.Cpu().String()
spinContainer.Requests.Memory = container.Resources.Requests.Memory().String()
spinContainer.Limits.CPU = container.Resources.Limits.Cpu().String()
spinContainer.Limits.Memory = container.Resources.Limits.Memory().String()

// appends all of the ports on the deployment type into the spinnaker definition
for _, port := range container.Ports {
spinContainer.Ports = append(spinContainer.Ports, types.Port{
ContainerPort: port.ContainerPort,
Name: port.Name,
Protocol: string(port.Protocol),
})
}

// appends all of the environment variables on the deployment type into the spinnaker definition
for _, env := range container.Env {
var e types.EnvVar
e.Name = env.Name
e.Value = env.Value

// appends all of the environment variables on the deployment type into the spinnaker definition
for _, env := range container.Env {
var e types.EnvVar
e.Name = env.Name
e.Value = env.Value

if vf := env.ValueFrom; vf != nil {
if vf.ConfigMapKeyRef != nil {
if vf.ConfigMapKeyRef.Optional == nil {
vf.ConfigMapKeyRef.Optional = newFalse()
}

e.EnvSource = &types.EnvSource{
ConfigMapSource: &types.ConfigMapSource{
ConfigMapName: vf.ConfigMapKeyRef.Name,
Key: vf.ConfigMapKeyRef.Key,
Optional: *vf.ConfigMapKeyRef.Optional,
},
}
if vf := env.ValueFrom; vf != nil {
if vf.ConfigMapKeyRef != nil {
if vf.ConfigMapKeyRef.Optional == nil {
vf.ConfigMapKeyRef.Optional = newFalse()
}

if vf.SecretKeyRef != nil {
if vf.SecretKeyRef.Optional == nil {
vf.SecretKeyRef.Optional = newFalse()
}
e.EnvSource = &types.EnvSource{
SecretSource: &types.SecretSource{
Key: vf.SecretKeyRef.Key,
SecretName: vf.SecretKeyRef.Name,
Optional: *vf.SecretKeyRef.Optional,
},
}
e.EnvSource = &types.EnvSource{
ConfigMapSource: &types.ConfigMapSource{
ConfigMapName: vf.ConfigMapKeyRef.Name,
Key: vf.ConfigMapKeyRef.Key,
Optional: *vf.ConfigMapKeyRef.Optional,
},
}
}

spinContainer.EnvVars = append(spinContainer.EnvVars, e)
if vf.SecretKeyRef != nil {
if vf.SecretKeyRef.Optional == nil {
vf.SecretKeyRef.Optional = newFalse()
}
e.EnvSource = &types.EnvSource{
SecretSource: &types.SecretSource{
Key: vf.SecretKeyRef.Key,
SecretName: vf.SecretKeyRef.Name,
Optional: *vf.SecretKeyRef.Optional,
},
}
}
}

for _, envFrom := range container.EnvFrom {
var e types.EnvFromSource
e.Prefix = envFrom.Prefix
spinContainer.EnvVars = append(spinContainer.EnvVars, e)
}

if cmRef := envFrom.ConfigMapRef; cmRef != nil {
e.ConfigMapSource = &types.EnvFromConfigMapSource{
Name: cmRef.Name,
}
}
for _, envFrom := range container.EnvFrom {
var e types.EnvFromSource
e.Prefix = envFrom.Prefix

if secRef := envFrom.SecretRef; secRef != nil {
e.SecretSource = &types.EnvFromSecretSource{
Name: secRef.Name,
}
if cmRef := envFrom.ConfigMapRef; cmRef != nil {
e.ConfigMapSource = &types.EnvFromConfigMapSource{
Name: cmRef.Name,
}

spinContainer.EnvFrom = append(spinContainer.EnvFrom, e)
}

if probe := container.LivenessProbe; probe != nil {
spinContainer.LivenessProbe = spinnakerProbeHandler(probe)
if secRef := envFrom.SecretRef; secRef != nil {
e.SecretSource = &types.EnvFromSecretSource{
Name: secRef.Name,
}
}

if probe := container.ReadinessProbe; probe != nil {
spinContainer.ReadinessProbe = spinnakerProbeHandler(probe)
}
spinContainer.EnvFrom = append(spinContainer.EnvFrom, e)
}

// add all of the volume mounts
for _, vm := range container.VolumeMounts {
spinContainer.VolumeMounts = append(spinContainer.VolumeMounts, types.VolumeMount{
Name: vm.Name,
ReadOnly: vm.ReadOnly,
MountPath: vm.MountPath,
})
}
if probe := container.LivenessProbe; probe != nil {
spinContainer.LivenessProbe = spinnakerProbeHandler(probe)
}

c = append(c, spinContainer)
if probe := container.ReadinessProbe; probe != nil {
spinContainer.ReadinessProbe = spinnakerProbeHandler(probe)
}

return c
// add all of the volume mounts
for _, vm := range container.VolumeMounts {
spinContainer.VolumeMounts = append(spinContainer.VolumeMounts, types.VolumeMount{
Name: vm.Name,
ReadOnly: vm.ReadOnly,
MountPath: vm.MountPath,
})
}

return spinContainer
}

func spinnakerProbeHandler(probe *corev1.Probe) *types.Probe {
Expand Down
51 changes: 51 additions & 0 deletions pipeline/builder/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func TestContainersFromManifests(t *testing.T) {
require.NoError(t, err, "error on retrieving the deployment manifests")

assert.Len(t, group.Containers, 1)
assert.Len(t, group.InitContainers, 1)
assert.Len(t, group.Annotations, 2)
assert.Equal(t, "fake-namespace", group.Namespace)

Expand Down Expand Up @@ -173,6 +174,7 @@ func TestContainersFromManifests(t *testing.T) {
assert.Equal(t, true, container.EnvVars[1].EnvSource.ConfigMapSource.Optional)
assert.Equal(t, false, container.EnvVars[2].EnvSource.ConfigMapSource.Optional)
})

t.Run("LivenessProbe is copied in the correct format", func(t *testing.T) {
file := filepath.Join(wd, "testdata", "deployment.probes.yml")
parser := builder.NewManfifestParser(&config.Pipeline{})
Expand All @@ -186,4 +188,53 @@ func TestContainersFromManifests(t *testing.T) {
require.NotNil(t, container.LivenessProbe)
require.NotNil(t, container.ReadinessProbe)
})

t.Run("InitContainers are copied in the correct format", func(t *testing.T) {
file := filepath.Join(wd, "testdata", "deployment.initContainer.yml")
parser := builder.NewManfifestParser(&config.Pipeline{
ImageDescriptions: []config.ImageDescription{
{
Name: "test-ref",
ImageID: "this-is-the-init-image-id",
},
},
})

group, err := parser.ContainersFromScaffold(scaffoldMock{
manifest: file,
imageDescriptionRefs: []config.ImageDescriptionRef{
{
Name: "test-ref",
ContainerName: "init-container",
},
},
})
require.NoError(t, err)

initContainer := group.InitContainers[0]

assert.Equal(t, "init-container", initContainer.Name)

require.NotNil(t, initContainer.LivenessProbe)
require.NotNil(t, initContainer.ReadinessProbe)

t.Run("InitContainer env are copied in", func(t *testing.T) {
require.Len(t, initContainer.EnvVars, 1)
require.Nil(t, initContainer.EnvVars[0].EnvSource)
assert.Equal(t, "WHATS_THE_WORD", initContainer.EnvVars[0].Name)
assert.Equal(t, "bird is the word", initContainer.EnvVars[0].Value)
})

t.Run("InitContainer VolumeMounts are copied in", func(t *testing.T) {

require.Len(t, initContainer.VolumeMounts, 1)
assert.Equal(t, "configmap-volume", initContainer.VolumeMounts[0].Name)
assert.Equal(t, "/thisisthemount", initContainer.VolumeMounts[0].MountPath)
assert.Equal(t, true, initContainer.VolumeMounts[0].ReadOnly)
})

t.Run("InitContainer image descriptions are returned correctly", func(t *testing.T) {
assert.Equal(t, "this-is-the-init-image-id", initContainer.ImageDescription.ImageID)
})
})
}
13 changes: 13 additions & 0 deletions pipeline/builder/testdata/deployment.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ spec:
annotations:
test: "annotations"
spec:
initContainers:
- name: init-container
command:
- echo
- hello
env:
- name: WHATS_THE_WORD
value: "bird is the word"
image: bird.word/latest
volumeMounts:
- name: configmap-volume
mountPath: "/thisisthemount"
readOnly: true
containers:
- name: test-container
command:
Expand Down
35 changes: 35 additions & 0 deletions pipeline/builder/testdata/deployment.initContainer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: example
namespace: fake-namespace
annotations:
fake-annotation-1: "Hello"
fake-annotation-2: "World"
spec:
template:
metadata:
labels:
app: example
annotations:
test: "annotations"
spec:
initContainers:
- name: init-container
command:
- echo
- hello init container
env:
- name: WHATS_THE_WORD
value: "bird is the word"
image: init.container/latest
livenessProbe:
exec:
command: ["say", "hello"]
readinessProbe:
exec:
command: ["say", "hello"]
volumeMounts:
- name: configmap-volume
mountPath: "/thisisthemount"
readOnly: true
Loading

0 comments on commit 54f975d

Please sign in to comment.