From c9216437059ad0fb688511d8005444b69f745c4f Mon Sep 17 00:00:00 2001 From: Shivam Sandbhor Date: Thu, 4 Nov 2021 05:54:12 +0530 Subject: [PATCH] Add support for statefulset controller. (#1452) Signed-off-by: Shivam Sandbhor --- docs/user-guide.md | 13 +- pkg/transformer/kubernetes/k8sutils.go | 18 +- pkg/transformer/kubernetes/kubernetes.go | 47 +- pkg/transformer/kubernetes/kubernetes_test.go | 64 ++- script/test/cmd/tests_new.sh | 10 +- .../fixtures/statefulset/docker-compose.yaml | 33 ++ .../test/fixtures/statefulset/output-k8s.json | 249 +++++++++ .../test/fixtures/statefulset/output-os.json | 503 ++++++++++++++++++ 8 files changed, 930 insertions(+), 7 deletions(-) create mode 100644 script/test/fixtures/statefulset/docker-compose.yaml create mode 100644 script/test/fixtures/statefulset/output-k8s.json create mode 100644 script/test/fixtures/statefulset/output-os.json diff --git a/docs/user-guide.md b/docs/user-guide.md index 61754b3a..b0d2e690 100755 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -94,7 +94,7 @@ INFO OpenShift file "foo-buildconfig.yaml" created ## Alternative Conversions -The default `kompose` transformation will generate Kubernetes [Deployments](http://kubernetes.io/docs/user-guide/deployments/) and [Services](http://kubernetes.io/docs/user-guide/services/), in yaml format. You have alternative option to generate json with `-j`. Also, you can alternatively generate [Replication Controllers](http://kubernetes.io/docs/user-guide/replication-controller/) objects, [Daemon Sets](http://kubernetes.io/docs/admin/daemons/), or [Helm](https://github.com/helm/helm) charts. +The default `kompose` transformation will generate Kubernetes [Deployments](http://kubernetes.io/docs/user-guide/deployments/) and [Services](http://kubernetes.io/docs/user-guide/services/), in yaml format. You have alternative option to generate json with `-j`. Also, you can alternatively generate [Replication Controllers](http://kubernetes.io/docs/user-guide/replication-controller/) objects, [Daemon Sets](http://kubernetes.io/docs/admin/daemons/), [Statefulset](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) or [Helm](https://github.com/helm/helm) charts. ```sh $ kompose convert -j @@ -125,6 +125,17 @@ INFO Kubernetes file "web-daemonset.yaml" created The `*-daemonset.yaml` files contain the Daemon Set objects +```sh +$ kompose convert --controller statefulset +INFO Kubernetes file "db-service.yaml" created +INFO Kubernetes file "wordpress-service.yaml" created +INFO Kubernetes file "db-statefulset.yaml" created +INFO Kubernetes file "wordpress-statefulset.yaml" created +``` + +The `*statefulset-.yaml` files contain the Statefulset objects. + + If you want to generate a Chart to be used with [Helm](https://github.com/kubernetes/helm) simply do: ```sh diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 774150d9..82e2d44e 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -496,7 +496,7 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic volumesMount = append(volumesMount, TmpVolumesMount...) } - if pvc != nil { + if pvc != nil && opt.Controller != StatefulStateController { // Looping on the slice pvc instead of `*objects = append(*objects, pvc...)` // because the type of objects and pvc is different, but when doing append // one element at a time it gets converted to runtime.Object for objects slice @@ -530,7 +530,9 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic template.Spec.Containers[0].VolumeMounts = append(template.Spec.Containers[0].VolumeMounts, volumesMount...) template.Spec.Containers[0].Stdin = service.Stdin template.Spec.Containers[0].TTY = service.Tty - template.Spec.Volumes = append(template.Spec.Volumes, volumes...) + if opt.Controller != StatefulStateController || opt.Volumes == "configMap" { + template.Spec.Volumes = append(template.Spec.Volumes, volumes...) + } template.Spec.Affinity = ConfigAffinity(service) // Configure the HealthCheck template.Spec.Containers[0].LivenessProbe = configProbe(service.HealthChecks.Liveness) @@ -637,6 +639,18 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic objType.Spec.Strategy.Type = appsv1.RecreateDeploymentStrategyType case *deployapi.DeploymentConfig: objType.Spec.Strategy.Type = deployapi.DeploymentStrategyTypeRecreate + case *appsv1.StatefulSet: + // embed all PVCs inside the StatefulSet object + if opt.Volumes == "configMap" { + break + } + persistentVolumeClaims := make([]api.PersistentVolumeClaim, len(pvc)) + for i, persistentVolumeClaim := range pvc { + persistentVolumeClaims[i] = *persistentVolumeClaim + persistentVolumeClaims[i].APIVersion = "" + persistentVolumeClaims[i].Kind = "" + } + objType.Spec.VolumeClaimTemplates = persistentVolumeClaims } } } diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index c271bebe..065319e7 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -63,6 +63,8 @@ const ( DeploymentController = "deployment" // DaemonSetController is controller type for DaemonSet DaemonSetController = "daemonset" + // StatefulStateController is controller type for StatefulSet + StatefulStateController = "statefulset" ) // CheckUnsupportedKey checks if given komposeObject contains @@ -425,6 +427,37 @@ func (k *Kubernetes) InitDS(name string, service kobject.ServiceConfig) *appsv1. return ds } +func (k *Kubernetes) InitSS(name string, service kobject.ServiceConfig, replicas int) *appsv1.StatefulSet { + var podSpec api.PodSpec + if len(service.Configs) > 0 { + podSpec = k.InitPodSpecWithConfigMap(name, service.Image, service) + } else { + podSpec = k.InitPodSpec(name, service.Image, service.ImagePullSecret) + } + rp := int32(replicas) + ds := &appsv1.StatefulSet{ + TypeMeta: metav1.TypeMeta{ + Kind: "StatefulSet", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: transformer.ConfigAllLabels(name, &service), + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &rp, + Template: api.PodTemplateSpec{ + Spec: podSpec, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: transformer.ConfigLabels(name), + }, + ServiceName: service.Name, + }, + } + return ds +} + func (k *Kubernetes) initIngress(name string, service kobject.ServiceConfig, port int32) *networkingv1.Ingress { hosts := regexp.MustCompile("[ ,]*,[ ,]*").Split(service.ExposeService, -1) @@ -1120,6 +1153,10 @@ func (k *Kubernetes) CreateWorkloadAndConfigMapObjects(name string, service kobj objects = append(objects, k.InitDS(name, service)) } + if opt.Controller == StatefulStateController { + objects = append(objects, k.InitSS(name, service, replica)) + } + if len(service.EnvFile) > 0 { for _, envFile := range service.EnvFile { configMap := k.InitConfigMapForEnv(name, opt, envFile) @@ -1280,7 +1317,6 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. allobjects = append(allobjects, item) } } - if opt.ServiceGroupMode != "" { log.Debugf("Service group mode is: %s", opt.ServiceGroupMode) komposeObjectToServiceConfigGroupMapping := KomposeObjectToServiceConfigGroupMapping(&komposeObject, opt) @@ -1413,6 +1449,9 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. objects = k.CreateWorkloadAndConfigMapObjects(name, service, opt) } + if opt.Controller == StatefulStateController { + service.ServiceType = "Headless" + } k.configKubeServiceAndIngressForService(service, name, &objects) err := k.UpdateKubernetesObjects(name, service, opt, &objects) @@ -1449,6 +1488,12 @@ func (k *Kubernetes) UpdateController(obj runtime.Object, updateTemplate func(*a return errors.Wrap(err, "updateTemplate failed") } updateMeta(&t.ObjectMeta) + case *appsv1.StatefulSet: + err = updateTemplate(&t.Spec.Template) + if err != nil { + return errors.Wrap(err, "updateTemplate failed") + } + updateMeta(&t.ObjectMeta) case *deployapi.DeploymentConfig: err = updateTemplate(t.Spec.Template) if err != nil { diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go index ab04da0a..110b0c09 100644 --- a/pkg/transformer/kubernetes/kubernetes_test.go +++ b/pkg/transformer/kubernetes/kubernetes_test.go @@ -303,7 +303,9 @@ func TestKomposeConvert(t *testing.T) { // objects generated are deployment, daemonset, ReplicationController, service and pvc "Convert to D, DS, and RC": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, CreateDS: true, CreateRC: true, Replicas: replicas, IsReplicaSetFlag: true}, 7}, "Convert to D, DS, and RC with v3 replicas": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, CreateDS: true, CreateRC: true}, 7}, - // TODO: add more tests + // objects generated are statefulset + "Convert to SS with replicas ": {newKomposeObject(), kobject.ConvertOptions{Controller: StatefulStateController, Replicas: replicas, IsReplicaSetFlag: true}, 5}, + "Convert to SS without replicas": {newKomposeObject(), kobject.ConvertOptions{Controller: StatefulStateController}, 5}, } for name, test := range testCases { @@ -318,7 +320,7 @@ func TestKomposeConvert(t *testing.T) { t.Errorf("Expected %d objects returned, got %d", test.expectedNumObjs, len(objs)) } - var foundSVC, foundD, foundDS, foundDC bool + var foundSVC, foundD, foundDS, foundDC, foundSS bool name := "app" labels := transformer.ConfigLabels(name) config := test.komposeObject.ServiceConfigs[name] @@ -334,6 +336,7 @@ func TestKomposeConvert(t *testing.T) { } foundSVC = true } + if test.opt.CreateD { if d, ok := obj.(*appsv1.Deployment); ok { if err := checkPodTemplate(config, d.Spec.Template, labelsWithNetwork); err != nil { @@ -426,6 +429,60 @@ func TestKomposeConvert(t *testing.T) { } } + if test.opt.Controller == StatefulStateController { + if ss, ok := obj.(*appsv1.StatefulSet); ok { + if err := checkPodTemplate(config, ss.Spec.Template, labelsWithNetwork); err != nil { + t.Errorf("%v", err) + } + if err := checkMeta(config, ss.ObjectMeta, name, true); err != nil { + t.Errorf("%v", err) + } + if test.opt.IsReplicaSetFlag { + if (int)(*ss.Spec.Replicas) != replicas { + t.Errorf("Expected %d replicas, got %d", replicas, ss.Spec.Replicas) + } + } else { + if (int)(*ss.Spec.Replicas) != newServiceConfig().Replicas { + t.Errorf("Expected %d replicas, got %d", newServiceConfig().Replicas, ss.Spec.Replicas) + } + } + foundSS = true + } + + if u, ok := obj.(*unstructured.Unstructured); ok { + if u.GetKind() == "Statefulset" { + u.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "apps", + Version: "v1", + Kind: "Statefulset", + }) + data, err := json.Marshal(u) + if err != nil { + t.Errorf("%v", err) + } + var d appsv1.Deployment + if err := json.Unmarshal(data, &d); err == nil { + if err := checkPodTemplate(config, d.Spec.Template, labelsWithNetwork); err != nil { + t.Errorf("%v", err) + } + if err := checkMeta(config, d.ObjectMeta, name, true); err != nil { + t.Errorf("%v", err) + } + if test.opt.IsReplicaSetFlag { + if (int)(*d.Spec.Replicas) != replicas { + t.Errorf("Expected %d replicas, got %d", replicas, d.Spec.Replicas) + } + } else { + if (int)(*d.Spec.Replicas) != newServiceConfig().Replicas { + t.Errorf("Expected %d replicas, got %d", newServiceConfig().Replicas, d.Spec.Replicas) + } + } + foundSS = true + } + } + } + } + // TODO: k8s & openshift transformer is now separated; either separate the test or combine the transformer if test.opt.CreateDeploymentConfig { if dc, ok := obj.(*deployapi.DeploymentConfig); ok { @@ -455,6 +512,9 @@ func TestKomposeConvert(t *testing.T) { t.Errorf("Expected create Daemon Set: %v, found Daemon Set: %v", test.opt.CreateDS, foundDS) } + if test.opt.Controller == StatefulStateController && !foundSS { + t.Errorf("Expected create StatefulStateController") + } if test.opt.CreateDeploymentConfig != foundDC { t.Errorf("Expected create Deployment Config: %v, found Deployment Config: %v", test.opt.CreateDeploymentConfig, foundDC) } diff --git a/script/test/cmd/tests_new.sh b/script/test/cmd/tests_new.sh index 16fa3863..980b9cf0 100755 --- a/script/test/cmd/tests_new.sh +++ b/script/test/cmd/tests_new.sh @@ -178,4 +178,12 @@ os_cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/healt k8s_output="$KOMPOSE_ROOT/script/test/fixtures/healthcheck/output-healthcheck-k8s.json" os_output="$KOMPOSE_ROOT/script/test/fixtures/healthcheck/output-healthcheck-os.json" convert::expect_success "$k8s_cmd" "$k8s_output" -convert::expect_success "$os_cmd" "$os_output" \ No newline at end of file +convert::expect_success "$os_cmd" "$os_output" + +# test statefulset +k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/statefulset/docker-compose.yaml convert --stdout -j --with-kompose-annotation=false --controller statefulset" +ocp_cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/statefulset/docker-compose.yaml convert --stdout -j --with-kompose-annotation=false --controller statefulset" +k8s_output="$KOMPOSE_ROOT/script/test/fixtures/statefulset/output-k8s.json" +ocp_output="$KOMPOSE_ROOT/script/test/fixtures/statefulset/output-os.json" +convert::expect_success "$k8s_cmd" "$k8s_output" +convert::expect_success "$ocp_cmd" "$ocp_output" diff --git a/script/test/fixtures/statefulset/docker-compose.yaml b/script/test/fixtures/statefulset/docker-compose.yaml new file mode 100644 index 00000000..489a2c43 --- /dev/null +++ b/script/test/fixtures/statefulset/docker-compose.yaml @@ -0,0 +1,33 @@ +version: "3" + +services: + db: + image: mysql:5.7 + volumes: + - db_data:/var/lib/mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: somewordpress + MYSQL_DATABASE: wordpress + MYSQL_USER: wordpress + MYSQL_PASSWORD: wordpress + ports: + - "3306:3306" + + wordpress: + depends_on: + - db + image: wordpress:latest + volumes: + - wordpress_data:/var/www/html + ports: + - "8000:80" + restart: always + environment: + WORDPRESS_DB_HOST: db:3306 + WORDPRESS_DB_USER: wordpress + WORDPRESS_DB_PASSWORD: wordpress + WORDPRESS_DB_NAME: wordpress +volumes: + db_data: {} + wordpress_data: {} \ No newline at end of file diff --git a/script/test/fixtures/statefulset/output-k8s.json b/script/test/fixtures/statefulset/output-k8s.json new file mode 100644 index 00000000..5c1e339a --- /dev/null +++ b/script/test/fixtures/statefulset/output-k8s.json @@ -0,0 +1,249 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "db", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "ports": [ + { + "name": "3306", + "port": 3306, + "targetPort": 3306 + } + ], + "selector": { + "io.kompose.service": "db" + }, + "clusterIP": "None", + "type": "ClusterIP" + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "wordpress", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "ports": [ + { + "name": "8000", + "port": 8000, + "targetPort": 80 + } + ], + "selector": { + "io.kompose.service": "wordpress" + }, + "clusterIP": "None", + "type": "ClusterIP" + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "StatefulSet", + "apiVersion": "apps/v1", + "metadata": { + "name": "db", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "io.kompose.service": "db" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "containers": [ + { + "name": "db", + "image": "mysql:5.7", + "ports": [ + { + "containerPort": 3306 + } + ], + "env": [ + { + "name": "MYSQL_DATABASE", + "value": "wordpress" + }, + { + "name": "MYSQL_PASSWORD", + "value": "wordpress" + }, + { + "name": "MYSQL_ROOT_PASSWORD", + "value": "somewordpress" + }, + { + "name": "MYSQL_USER", + "value": "wordpress" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "db-data", + "mountPath": "/var/lib/mysql" + } + ] + } + ], + "restartPolicy": "Always" + } + }, + "volumeClaimTemplates": [ + { + "metadata": { + "name": "db-data", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db-data" + } + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "100Mi" + } + } + }, + "status": {} + } + ], + "serviceName": "db", + "updateStrategy": {} + }, + "status": { + "replicas": 0 + } + }, + { + "kind": "StatefulSet", + "apiVersion": "apps/v1", + "metadata": { + "name": "wordpress", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "io.kompose.service": "wordpress" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "containers": [ + { + "name": "wordpress", + "image": "wordpress:latest", + "ports": [ + { + "containerPort": 80 + } + ], + "env": [ + { + "name": "WORDPRESS_DB_HOST", + "value": "db:3306" + }, + { + "name": "WORDPRESS_DB_NAME", + "value": "wordpress" + }, + { + "name": "WORDPRESS_DB_PASSWORD", + "value": "wordpress" + }, + { + "name": "WORDPRESS_DB_USER", + "value": "wordpress" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "wordpress-data", + "mountPath": "/var/www/html" + } + ] + } + ], + "restartPolicy": "Always" + } + }, + "volumeClaimTemplates": [ + { + "metadata": { + "name": "wordpress-data", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress-data" + } + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "100Mi" + } + } + }, + "status": {} + } + ], + "serviceName": "wordpress", + "updateStrategy": {} + }, + "status": { + "replicas": 0 + } + } + ] +} diff --git a/script/test/fixtures/statefulset/output-os.json b/script/test/fixtures/statefulset/output-os.json new file mode 100644 index 00000000..0c40d367 --- /dev/null +++ b/script/test/fixtures/statefulset/output-os.json @@ -0,0 +1,503 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "db", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "ports": [ + { + "name": "3306", + "port": 3306, + "targetPort": 3306 + } + ], + "selector": { + "io.kompose.service": "db" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "wordpress", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "ports": [ + { + "name": "8000", + "port": 8000, + "targetPort": 80 + } + ], + "selector": { + "io.kompose.service": "wordpress" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "StatefulSet", + "apiVersion": "apps/v1", + "metadata": { + "name": "db", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "io.kompose.service": "db" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "containers": [ + { + "name": "db", + "image": "mysql:5.7", + "ports": [ + { + "containerPort": 3306 + } + ], + "env": [ + { + "name": "MYSQL_DATABASE", + "value": "wordpress" + }, + { + "name": "MYSQL_PASSWORD", + "value": "wordpress" + }, + { + "name": "MYSQL_ROOT_PASSWORD", + "value": "somewordpress" + }, + { + "name": "MYSQL_USER", + "value": "wordpress" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "db-data", + "mountPath": "/var/lib/mysql" + } + ] + } + ], + "restartPolicy": "Always" + } + }, + "volumeClaimTemplates": [ + { + "metadata": { + "name": "db-data", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db-data" + } + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "100Mi" + } + } + }, + "status": {} + } + ], + "serviceName": "db", + "updateStrategy": {} + }, + "status": { + "replicas": 0 + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "db", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "strategy": { + "type": "Recreate", + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "db" + ], + "from": { + "kind": "ImageStreamTag", + "name": "db:5.7" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "db" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "containers": [ + { + "name": "db", + "image": " ", + "ports": [ + { + "containerPort": 3306 + } + ], + "env": [ + { + "name": "MYSQL_DATABASE", + "value": "wordpress" + }, + { + "name": "MYSQL_PASSWORD", + "value": "wordpress" + }, + { + "name": "MYSQL_ROOT_PASSWORD", + "value": "somewordpress" + }, + { + "name": "MYSQL_USER", + "value": "wordpress" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "db-data", + "mountPath": "/var/lib/mysql" + } + ] + } + ], + "restartPolicy": "Always" + } + } + }, + "status": { + "latestVersion": 0, + "observedGeneration": 0, + "replicas": 0, + "updatedReplicas": 0, + "availableReplicas": 0, + "unavailableReplicas": 0 + } + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "db", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "db" + } + }, + "spec": { + "lookupPolicy": { + "local": false + }, + "tags": [ + { + "name": "", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "mysql:5.7" + }, + "generation": null, + "importPolicy": {}, + "referencePolicy": { + "type": "" + } + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "StatefulSet", + "apiVersion": "apps/v1", + "metadata": { + "name": "wordpress", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "io.kompose.service": "wordpress" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "containers": [ + { + "name": "wordpress", + "image": "wordpress:latest", + "ports": [ + { + "containerPort": 80 + } + ], + "env": [ + { + "name": "WORDPRESS_DB_HOST", + "value": "db:3306" + }, + { + "name": "WORDPRESS_DB_NAME", + "value": "wordpress" + }, + { + "name": "WORDPRESS_DB_PASSWORD", + "value": "wordpress" + }, + { + "name": "WORDPRESS_DB_USER", + "value": "wordpress" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "wordpress-data", + "mountPath": "/var/www/html" + } + ] + } + ], + "restartPolicy": "Always" + } + }, + "volumeClaimTemplates": [ + { + "metadata": { + "name": "wordpress-data", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress-data" + } + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "100Mi" + } + } + }, + "status": {} + } + ], + "serviceName": "wordpress", + "updateStrategy": {} + }, + "status": { + "replicas": 0 + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "wordpress", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "strategy": { + "type": "Recreate", + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "wordpress" + ], + "from": { + "kind": "ImageStreamTag", + "name": "wordpress:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "wordpress" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "containers": [ + { + "name": "wordpress", + "image": " ", + "ports": [ + { + "containerPort": 80 + } + ], + "env": [ + { + "name": "WORDPRESS_DB_HOST", + "value": "db:3306" + }, + { + "name": "WORDPRESS_DB_NAME", + "value": "wordpress" + }, + { + "name": "WORDPRESS_DB_PASSWORD", + "value": "wordpress" + }, + { + "name": "WORDPRESS_DB_USER", + "value": "wordpress" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "wordpress-data", + "mountPath": "/var/www/html" + } + ] + } + ], + "restartPolicy": "Always" + } + } + }, + "status": { + "latestVersion": 0, + "observedGeneration": 0, + "replicas": 0, + "updatedReplicas": 0, + "availableReplicas": 0, + "unavailableReplicas": 0 + } + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "wordpress", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "wordpress" + } + }, + "spec": { + "lookupPolicy": { + "local": false + }, + "tags": [ + { + "name": "", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "wordpress:latest" + }, + "generation": null, + "importPolicy": {}, + "referencePolicy": { + "type": "" + } + } + ] + }, + "status": { + "dockerImageRepository": "" + } + } + ] +}