Skip to content

Commit

Permalink
Merge pull request #156 from appilon/61-init-containers
Browse files Browse the repository at this point in the history
r/kubernetes_pod: add support for init containers
  • Loading branch information
appilon authored May 2, 2018
2 parents 2564c5e + ab98f18 commit 0c0e4cf
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 13 deletions.
134 changes: 125 additions & 9 deletions kubernetes/resource_kubernetes_pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import (
)

func TestAccKubernetesPod_basic(t *testing.T) {
var conf api.Pod
var conf1 api.Pod
var conf2 api.Pod

podName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
secretName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
Expand All @@ -32,7 +33,7 @@ func TestAccKubernetesPod_basic(t *testing.T) {
{
Config: testAccKubernetesPodConfigBasic(secretName, configMapName, podName, imageName1),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf),
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf1),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.annotations.%", "0"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.labels.%", "1"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.labels.app", "pod_label"),
Expand All @@ -49,16 +50,72 @@ func TestAccKubernetesPod_basic(t *testing.T) {
{
Config: testAccKubernetesPodConfigBasic(secretName, configMapName, podName, imageName2),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf),
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf2),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.container.0.image", imageName2),
testAccCheckKubernetesPodForceNew(&conf1, &conf2, false),
),
},
},
})
}

func TestAccKubernetesPod_initContainer_updateForcesNew(t *testing.T) {
var conf1 api.Pod
var conf2 api.Pod

podName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
image1 := "busybox:1.27"
image2 := "busybox:1.28"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesPodDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesPodConfigWithInitContainer(podName, image1),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf1),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.annotations.%", "0"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.labels.%", "1"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.labels.app", "pod_label"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.name", podName),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.name", "install"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.image", image1),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.command.0", "wget"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.command.1", "-O"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.command.2", "/work-dir/index.html"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.command.3", "http://kubernetes.io"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.volume_mount.0.name", "workdir"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.volume_mount.0.mount_path", "/work-dir"),
),
},
{
Config: testAccKubernetesPodConfigWithInitContainer(podName, image2),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf2),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.annotations.%", "0"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.labels.%", "1"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.labels.app", "pod_label"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.name", podName),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.name", "install"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.image", image2),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.command.0", "wget"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.command.1", "-O"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.command.2", "/work-dir/index.html"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.command.3", "http://kubernetes.io"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.volume_mount.0.name", "workdir"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.init_container.0.volume_mount.0.mount_path", "/work-dir"),
testAccCheckKubernetesPodForceNew(&conf1, &conf2, true),
),
},
},
})
}

func TestAccKubernetesPod_updateArgsForceNew(t *testing.T) {
var conf api.Pod
var conf1 api.Pod
var conf2 api.Pod

podName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))

Expand All @@ -74,7 +131,7 @@ func TestAccKubernetesPod_updateArgsForceNew(t *testing.T) {
{
Config: testAccKubernetesPodConfigArgsUpdate(podName, imageName, argsBefore),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf),
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf1),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.annotations.%", "0"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.name", podName),
resource.TestCheckResourceAttrSet("kubernetes_pod.test", "metadata.0.generation"),
Expand All @@ -91,7 +148,7 @@ func TestAccKubernetesPod_updateArgsForceNew(t *testing.T) {
{
Config: testAccKubernetesPodConfigArgsUpdate(podName, imageName, argsAfter),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf),
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf2),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.annotations.%", "0"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.name", podName),
resource.TestCheckResourceAttrSet("kubernetes_pod.test", "metadata.0.generation"),
Expand All @@ -103,14 +160,16 @@ func TestAccKubernetesPod_updateArgsForceNew(t *testing.T) {
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.container.0.args.0", "-listen=:80"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.container.0.args.1", "-text='after modification'"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.container.0.name", "containername"),
testAccCheckKubernetesPodForceNew(&conf1, &conf2, true),
),
},
},
})
}

func TestAccKubernetesPod_updateEnvForceNew(t *testing.T) {
var conf api.Pod
var conf1 api.Pod
var conf2 api.Pod

podName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))

Expand All @@ -126,7 +185,7 @@ func TestAccKubernetesPod_updateEnvForceNew(t *testing.T) {
{
Config: testAccKubernetesPodConfigEnvUpdate(podName, imageName, envBefore),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf),
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf1),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.annotations.%", "0"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.name", podName),
resource.TestCheckResourceAttrSet("kubernetes_pod.test", "metadata.0.generation"),
Expand All @@ -143,7 +202,7 @@ func TestAccKubernetesPod_updateEnvForceNew(t *testing.T) {
{
Config: testAccKubernetesPodConfigEnvUpdate(podName, imageName, envAfter),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf),
testAccCheckKubernetesPodExists("kubernetes_pod.test", &conf2),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.annotations.%", "0"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "metadata.0.name", podName),
resource.TestCheckResourceAttrSet("kubernetes_pod.test", "metadata.0.generation"),
Expand All @@ -155,6 +214,7 @@ func TestAccKubernetesPod_updateEnvForceNew(t *testing.T) {
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.container.0.env.0.name", "foo"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.container.0.env.0.value", "baz"),
resource.TestCheckResourceAttr("kubernetes_pod.test", "spec.0.container.0.name", "containername"),
testAccCheckKubernetesPodForceNew(&conf1, &conf2, true),
),
},
},
Expand Down Expand Up @@ -566,6 +626,21 @@ func testAccCheckKubernetesPodExists(n string, obj *api.Pod) resource.TestCheckF
}
}

func testAccCheckKubernetesPodForceNew(old, new *api.Pod, wantNew bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
if wantNew {
if old.ObjectMeta.UID == new.ObjectMeta.UID {
return fmt.Errorf("Expecting new resource for pod %s", old.ObjectMeta.UID)
}
} else {
if old.ObjectMeta.UID != new.ObjectMeta.UID {
return fmt.Errorf("Expecting pod UIDs to be the same: expected %s got %s", old.ObjectMeta.UID, new.ObjectMeta.UID)
}
}
return nil
}
}

func testAccKubernetesPodConfigBasic(secretName, configMapName, podName, imageName string) string {
return fmt.Sprintf(`
Expand Down Expand Up @@ -636,6 +711,47 @@ resource "kubernetes_pod" "test" {
`, secretName, configMapName, podName, imageName)
}

func testAccKubernetesPodConfigWithInitContainer(podName string, image string) string {
return fmt.Sprintf(`
resource "kubernetes_pod" "test" {
metadata {
labels {
app = "pod_label"
}
name = "%s"
}
spec {
container {
name = "nginx"
image = "nginx"
port {
container_port = 80
}
volume_mount {
name = "workdir"
mount_path = "/usr/share/nginx/html"
}
}
init_container {
name = "install"
image = "%s"
command = ["wget", "-O", "/work-dir/index.html", "http://kubernetes.io"]
volume_mount {
name = "workdir"
mount_path = "/work-dir"
}
}
dns_policy = "Default"
volume {
name = "workdir"
empty_dir {}
}
}
}
`, podName, image)
}

func testAccKubernetesPodConfigWithSecurityContext(podName, imageName string) string {
return fmt.Sprintf(`
resource "kubernetes_pod" "test" {
Expand Down
81 changes: 81 additions & 0 deletions kubernetes/resource_kubernetes_replication_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,34 @@ func TestAccKubernetesReplicationController_basic(t *testing.T) {
})
}

func TestAccKubernetesReplicationController_initContainer(t *testing.T) {
var conf api.ReplicationController
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "kubernetes_replication_controller.test",
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesReplicationControllerDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesReplicationControllerConfig_initContainer(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesReplicationControllerExists("kubernetes_replication_controller.test", &conf),
resource.TestCheckResourceAttr("kubernetes_replication_controller.test", "spec.0.template.0.init_container.0.image", "busybox"),
resource.TestCheckResourceAttr("kubernetes_replication_controller.test", "spec.0.template.0.init_container.0.name", "install"),
resource.TestCheckResourceAttr("kubernetes_replication_controller.test", "spec.0.template.0.init_container.0.command.0", "wget"),
resource.TestCheckResourceAttr("kubernetes_replication_controller.test", "spec.0.template.0.init_container.0.command.1", "-O"),
resource.TestCheckResourceAttr("kubernetes_replication_controller.test", "spec.0.template.0.init_container.0.command.2", "/work-dir/index.html"),
resource.TestCheckResourceAttr("kubernetes_replication_controller.test", "spec.0.template.0.init_container.0.command.3", "http://kubernetes.io"),
resource.TestCheckResourceAttr("kubernetes_replication_controller.test", "spec.0.template.0.init_container.0.volume_mount.0.name", "workdir"),
resource.TestCheckResourceAttr("kubernetes_replication_controller.test", "spec.0.template.0.init_container.0.volume_mount.0.mount_path", "/work-dir"),
),
},
},
})
}

func TestAccKubernetesReplicationController_importBasic(t *testing.T) {
resourceName := "kubernetes_replication_controller.test"
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
Expand Down Expand Up @@ -469,6 +497,59 @@ resource "kubernetes_replication_controller" "test" {
`, name)
}

func testAccKubernetesReplicationControllerConfig_initContainer(name string) string {
return fmt.Sprintf(`
resource "kubernetes_replication_controller" "test" {
metadata {
annotations {
TestAnnotationOne = "one"
TestAnnotationTwo = "two"
}
labels {
TestLabelOne = "one"
TestLabelTwo = "two"
TestLabelThree = "three"
}
name = "%s"
}
spec {
replicas = 1000 # This is intentionally high to exercise the waiter
selector {
TestLabelOne = "one"
TestLabelTwo = "two"
TestLabelThree = "three"
}
template {
container {
name = "nginx"
image = "nginx"
port {
container_port = 80
}
volume_mount {
name = "workdir"
mount_path = "/usr/share/nginx/html"
}
}
init_container {
name = "install"
image = "busybox"
command = ["wget", "-O", "/work-dir/index.html", "http://kubernetes.io"]
volume_mount {
name = "workdir"
mount_path = "/work-dir"
}
}
dns_policy = "Default"
volume {
name = "workdir"
empty_dir {}
}
}
}
}`, name)
}

func testAccKubernetesReplicationControllerConfig_modified(name string) string {
return fmt.Sprintf(`
resource "kubernetes_replication_controller" "test" {
Expand Down
6 changes: 3 additions & 3 deletions kubernetes/schema_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func volumeMountFields() map[string]*schema.Schema {
}
}

func containerFields(isUpdatable bool) map[string]*schema.Schema {
func containerFields(isUpdatable, isInitContainer bool) map[string]*schema.Schema {
s := map[string]*schema.Schema{
"args": {
Type: schema.TypeList,
Expand Down Expand Up @@ -477,8 +477,8 @@ func containerFields(isUpdatable bool) map[string]*schema.Schema {

if !isUpdatable {
for k, _ := range s {
if k == "image" {
// This field is always updatable
if k == "image" && !isInitContainer {
// this field is updatable for non-init containers
continue
}
s[k].ForceNew = true
Expand Down
11 changes: 10 additions & 1 deletion kubernetes/schema_pod_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@ func podSpecFields(isUpdatable bool) map[string]*schema.Schema {
Optional: true,
Description: "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/containers",
Elem: &schema.Resource{
Schema: containerFields(isUpdatable),
Schema: containerFields(isUpdatable, false),
},
},
"init_container": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Description: "List of init containers belonging to the pod. Init containers always run to completion and each must complete succesfully before the next is started. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/",
Elem: &schema.Resource{
Schema: containerFields(isUpdatable, true),
},
},
"dns_policy": {
Expand Down
15 changes: 15 additions & 0 deletions kubernetes/structures_pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@ func flattenPodSpec(in v1.PodSpec) ([]interface{}, error) {
if in.ActiveDeadlineSeconds != nil {
att["active_deadline_seconds"] = *in.ActiveDeadlineSeconds
}

containers, err := flattenContainers(in.Containers)
if err != nil {
return nil, err
}
att["container"] = containers

initContainers, err := flattenContainers(in.InitContainers)
if err != nil {
return nil, err
}
att["init_container"] = initContainers

att["dns_policy"] = in.DNSPolicy

att["host_ipc"] = in.HostIPC
Expand Down Expand Up @@ -325,6 +332,14 @@ func expandPodSpec(p []interface{}) (v1.PodSpec, error) {
obj.Containers = cs
}

if v, ok := in["init_container"].([]interface{}); ok && len(v) > 0 {
cs, err := expandContainers(v)
if err != nil {
return obj, err
}
obj.InitContainers = cs
}

if v, ok := in["dns_policy"].(string); ok {
obj.DNSPolicy = v1.DNSPolicy(v)
}
Expand Down
Loading

0 comments on commit 0c0e4cf

Please sign in to comment.