Feat add ns generation (#1667)

* feat: add ns generation for k8s

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>

* feat: add ns generation for openshift

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>

* test: add functional tests

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>

* fix: remove some code nits

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>

---------

Signed-off-by: AhmedGrati <ahmedgrati1999@gmail.com>
This commit is contained in:
AhmedGrati 2023-07-16 22:59:16 +01:00 committed by GitHub
parent 071451dfdf
commit b6b708b637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 291 additions and 1 deletions

View File

@ -47,6 +47,7 @@ var (
ConvertReplicas int ConvertReplicas int
ConvertController string ConvertController string
ConvertPushImage bool ConvertPushImage bool
ConvertNamespace string
ConvertPushImageRegistry string ConvertPushImageRegistry string
ConvertOpt kobject.ConvertOptions ConvertOpt kobject.ConvertOptions
ConvertYAMLIndent int ConvertYAMLIndent int
@ -122,6 +123,7 @@ var convertCmd = &cobra.Command{
GenerateNetworkPolicies: GenerateNetworkPolicies, GenerateNetworkPolicies: GenerateNetworkPolicies,
BuildCommand: BuildCommand, BuildCommand: BuildCommand,
PushCommand: PushCommand, PushCommand: PushCommand,
Namespace: ConvertNamespace,
} }
if ServiceGroupMode == "" && MultipleContainerMode { if ServiceGroupMode == "" && MultipleContainerMode {
@ -186,6 +188,7 @@ func init() {
convertCmd.Flags().IntVar(&ConvertReplicas, "replicas", 1, "Specify the number of replicas in the generated resource spec") convertCmd.Flags().IntVar(&ConvertReplicas, "replicas", 1, "Specify the number of replicas in the generated resource spec")
convertCmd.Flags().StringVar(&ConvertVolumes, "volumes", "persistentVolumeClaim", `Volumes to be generated ("persistentVolumeClaim"|"emptyDir"|"hostPath" | "configMap")`) convertCmd.Flags().StringVar(&ConvertVolumes, "volumes", "persistentVolumeClaim", `Volumes to be generated ("persistentVolumeClaim"|"emptyDir"|"hostPath" | "configMap")`)
convertCmd.Flags().StringVar(&ConvertPVCRequestSize, "pvc-request-size", "", `Specify the size of pvc storage requests in the generated resource spec`) convertCmd.Flags().StringVar(&ConvertPVCRequestSize, "pvc-request-size", "", `Specify the size of pvc storage requests in the generated resource spec`)
convertCmd.Flags().StringVarP(&ConvertNamespace, "namespace", "n", "", `Specify the namespace of the generated resources`)
convertCmd.Flags().BoolVar(&GenerateNetworkPolicies, "generate-network-policies", false, "Specify whether to generate network policies or not.") convertCmd.Flags().BoolVar(&GenerateNetworkPolicies, "generate-network-policies", false, "Specify whether to generate network policies or not.")
convertCmd.Flags().BoolVar(&WithKomposeAnnotation, "with-kompose-annotation", true, "Add kompose annotations to generated resource") convertCmd.Flags().BoolVar(&WithKomposeAnnotation, "with-kompose-annotation", true, "Add kompose annotations to generated resource")

View File

@ -219,6 +219,8 @@ func Convert(opt kobject.ConvertOptions) {
log.Fatalf(err.Error()) log.Fatalf(err.Error())
} }
komposeObject.Namespace = opt.Namespace
// Get a transformer that maps komposeObject to provider's primitives // Get a transformer that maps komposeObject to provider's primitives
t := getTransformer(opt) t := getTransformer(opt)

View File

@ -38,6 +38,9 @@ type KomposeObject struct {
LoadedFrom string LoadedFrom string
Secrets types.Secrets Secrets types.Secrets
// Namespace is the namespace where all the generated objects would be assigned to
Namespace string
} }
// ConvertOptions holds all options that controls transformation process // ConvertOptions holds all options that controls transformation process

View File

@ -1447,6 +1447,12 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
allobjects = append(allobjects, item) allobjects = append(allobjects, item)
} }
} }
if komposeObject.Namespace != "" {
ns := transformer.CreateNamespace(komposeObject.Namespace)
allobjects = append(allobjects, ns)
}
if opt.ServiceGroupMode != "" { if opt.ServiceGroupMode != "" {
log.Debugf("Service group mode is: %s", opt.ServiceGroupMode) log.Debugf("Service group mode is: %s", opt.ServiceGroupMode)
komposeObjectToServiceConfigGroupMapping := KomposeObjectToServiceConfigGroupMapping(&komposeObject, opt) komposeObjectToServiceConfigGroupMapping := KomposeObjectToServiceConfigGroupMapping(&komposeObject, opt)
@ -1596,6 +1602,7 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
// sort all object so Services are first // sort all object so Services are first
k.SortServicesFirst(&allobjects) k.SortServicesFirst(&allobjects)
k.RemoveDupObjects(&allobjects) k.RemoveDupObjects(&allobjects)
transformer.AssignNamespaceToObjects(&allobjects, komposeObject.Namespace)
// k.FixWorkloadVersion(&allobjects) // k.FixWorkloadVersion(&allobjects)
return allobjects, nil return allobjects, nil
} }

View File

@ -1094,3 +1094,28 @@ func TestServiceGroupModeImagePullSecrets(t *testing.T) {
} }
} }
} }
func TestNamespaceGeneration(t *testing.T) {
ns := "app"
komposeObject := kobject.KomposeObject{
ServiceConfigs: map[string]kobject.ServiceConfig{"app": newServiceConfig()},
Namespace: ns,
}
k := Kubernetes{}
objs, err := k.Transform(komposeObject, kobject.ConvertOptions{})
if err != nil {
t.Error(errors.Wrap(err, "k.Transform failed"))
}
for _, obj := range objs {
if namespace, ok := obj.(*api.Namespace); ok {
if strings.ToLower(ns) != strings.ToLower(namespace.ObjectMeta.Name) {
t.Errorf("Expected namespace name %v, got %v", ns, namespace.ObjectMeta.Name)
}
}
if dep, ok := obj.(*appsv1.Deployment); ok {
if dep.ObjectMeta.Namespace != ns {
t.Errorf("Expected deployment namespace %v, got %v", ns, dep.ObjectMeta.Namespace)
}
}
}
}

View File

@ -259,6 +259,12 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
} }
// this will hold all the converted data // this will hold all the converted data
var allobjects []runtime.Object var allobjects []runtime.Object
if komposeObject.Namespace != "" {
ns := transformer.CreateNamespace(komposeObject.Namespace)
allobjects = append(allobjects, ns)
}
var err error var err error
var composeFileDir string var composeFileDir string
buildRepo := opt.BuildRepo buildRepo := opt.BuildRepo
@ -422,6 +428,7 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
// sort all object so Services are first // sort all object so Services are first
o.SortServicesFirst(&allobjects) o.SortServicesFirst(&allobjects)
o.RemoveDupObjects(&allobjects) o.RemoveDupObjects(&allobjects)
transformer.AssignNamespaceToObjects(&allobjects, komposeObject.Namespace)
// o.FixWorkloadVersion(&allobjects) // o.FixWorkloadVersion(&allobjects)
return allobjects, nil return allobjects, nil

View File

@ -29,6 +29,7 @@ import (
"github.com/kubernetes/kompose/pkg/transformer/kubernetes" "github.com/kubernetes/kompose/pkg/transformer/kubernetes"
deployapi "github.com/openshift/api/apps/v1" deployapi "github.com/openshift/api/apps/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
api "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
) )
@ -465,3 +466,28 @@ func TestServiceExternalTrafficPolicy(t *testing.T) {
} }
} }
} }
func TestNamespaceGeneration(t *testing.T) {
ns := "app"
komposeObject := kobject.KomposeObject{
ServiceConfigs: map[string]kobject.ServiceConfig{"app": newServiceConfig()},
Namespace: ns,
}
o := OpenShift{}
objs, err := o.Transform(komposeObject, kobject.ConvertOptions{})
if err != nil {
t.Error(errors.Wrap(err, "k.Transform failed"))
}
for _, obj := range objs {
if namespace, ok := obj.(*api.Namespace); ok {
if strings.ToLower(ns) != strings.ToLower(namespace.ObjectMeta.Name) {
t.Errorf("Expected namespace name %v, got %v", ns, namespace.ObjectMeta.Name)
}
}
if dep, ok := obj.(*deployapi.DeploymentConfig); ok {
if dep.ObjectMeta.Namespace != ns {
t.Errorf("Expected deployment namespace %v, got %v", ns, dep.ObjectMeta.Namespace)
}
}
}
}

View File

@ -31,6 +31,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
) )
// Selector used as labels and selector // Selector used as labels and selector
@ -443,3 +445,33 @@ func PushDockerImageWithOpt(service kobject.ServiceConfig, serviceName string, o
return nil return nil
} }
// CreateNamespace creates a Kubernetes namespace, which can be used in both:
// Openshift and Kubernetes
func CreateNamespace(namespace string) *api.Namespace {
return &api.Namespace{
TypeMeta: metav1.TypeMeta{
Kind: "Namespace",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
},
}
}
// AssignNamespaceToObjects will add the namespace metadata to each object
func AssignNamespaceToObjects(objs *[]runtime.Object, namespace string) {
ns := "default"
if namespace != "" {
ns = namespace
}
var result []runtime.Object
for _, obj := range *objs {
if us, ok := obj.(metav1.Object); ok {
us.SetNamespace(ns)
}
result = append(result, obj)
}
*objs = result
}

View File

@ -271,11 +271,19 @@ convert::expect_success "$os_cmd" "$os_output"
# Test support for network policies generation # Test support for network policies generation
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/network-policies/docker-compose.yaml convert --generate-network-policies --stdout --with-kompose-annotation=false" k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/network-policies/docker-compose.yaml convert --generate-network-policies --stdout --with-kompose-annotation=false"
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/network-policies/output-k8s.yaml" k8s_output="$KOMPOSE_ROOT/script/test/fixtures/network-policies/output-k8s.yaml"
convert::expect_success "$os_cmd" "$os_output" convert::expect_success "$k8s_cmd" "$k8s_output"
# Test support for custom build and push images # Test support for custom build and push images
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/custom-build-push/docker-compose.yaml convert --build-command 'docker build -t ahmedgrati/kompose-test ./script/test/fixtures/custom-build-push' --push-command 'docker push ahmedgrati/kompose-test' --stdout --with-kompose-annotation=false" k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/custom-build-push/docker-compose.yaml convert --build-command 'docker build -t ahmedgrati/kompose-test ./script/test/fixtures/custom-build-push' --push-command 'docker push ahmedgrati/kompose-test' --stdout --with-kompose-annotation=false"
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/custom-build-push/output-k8s.yaml" k8s_output="$KOMPOSE_ROOT/script/test/fixtures/custom-build-push/output-k8s.yaml"
convert::expect_success "$k8s_cmd" "$k8s_output"
# Test support for namespace generation
k8s_cmd="kompose -f ./script/test/fixtures/namespace/docker-compose.yaml convert --stdout --with-kompose-annotation=false -n web"
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/namespace/output-k8s.yaml"
os_cmd="kompose -f ./script/test/fixtures/namespace/docker-compose.yaml convert --stdout --with-kompose-annotation=false -n web --provider openshift"
os_output="$KOMPOSE_ROOT/script/test/fixtures/namespace/output-os.yaml"
convert::expect_success "$k8s_cmd" "$k8s_output"
convert::expect_success "$os_cmd" "$os_output" convert::expect_success "$os_cmd" "$os_output"
# Test support for read only root fs # Test support for read only root fs

View File

@ -0,0 +1,6 @@
version: '3'
services:
web:
image: nginx
ports:
- 80:80

View File

@ -0,0 +1,62 @@
---
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
io.kompose.service: web
name: web
namespace: web
spec:
ports:
- name: "80"
port: 80
targetPort: 80
selector:
io.kompose.service: web
status:
loadBalancer: {}
---
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: web
namespace: web
spec: {}
status: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
io.kompose.service: web
name: web
namespace: web
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
io.kompose.network/namespace-default: "true"
io.kompose.service: web
spec:
containers:
- image: nginx
name: web
ports:
- containerPort: 80
hostPort: 80
protocol: TCP
resources: {}
restartPolicy: Always
status: {}

View File

@ -0,0 +1,104 @@
---
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
io.kompose.service: web
name: web
namespace: web
spec:
ports:
- name: "80"
port: 80
targetPort: 80
selector:
io.kompose.service: web
status:
loadBalancer: {}
---
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: web
namespace: web
spec: {}
status: {}
---
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
creationTimestamp: null
labels:
io.kompose.service: web
name: web
namespace: web
spec:
replicas: 1
selector:
io.kompose.service: web
strategy:
resources: {}
template:
metadata:
creationTimestamp: null
labels:
io.kompose.network/namespace-default: "true"
io.kompose.service: web
spec:
containers:
- image: ' '
name: web
ports:
- containerPort: 80
hostPort: 80
protocol: TCP
resources: {}
restartPolicy: Always
test: false
triggers:
- type: ConfigChange
- imageChangeParams:
automatic: true
containerNames:
- web
from:
kind: ImageStreamTag
name: web:latest
type: ImageChange
status:
availableReplicas: 0
latestVersion: 0
observedGeneration: 0
replicas: 0
unavailableReplicas: 0
updatedReplicas: 0
---
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
creationTimestamp: null
labels:
io.kompose.service: web
name: web
namespace: web
spec:
lookupPolicy:
local: false
tags:
- annotations: null
from:
kind: DockerImage
name: nginx
generation: null
importPolicy: {}
name: latest
referencePolicy:
type: ""
status:
dockerImageRepository: ""

View File

@ -6,6 +6,7 @@ metadata:
labels: labels:
io.kompose.service: test io.kompose.service: test
name: test name: test
namespace: default
spec: spec:
ports: ports:
- name: "80" - name: "80"
@ -24,6 +25,7 @@ metadata:
labels: labels:
io.kompose.service: test io.kompose.service: test
name: test name: test
namespace: default
spec: spec:
replicas: 1 replicas: 1
selector: selector:

View File

@ -6,6 +6,7 @@ metadata:
labels: labels:
io.kompose.service: test io.kompose.service: test
name: test name: test
namespace: default
spec: spec:
ports: ports:
- name: "80" - name: "80"
@ -24,6 +25,7 @@ metadata:
labels: labels:
io.kompose.service: test io.kompose.service: test
name: test name: test
namespace: default
spec: spec:
replicas: 1 replicas: 1
selector: selector:
@ -75,6 +77,7 @@ metadata:
labels: labels:
io.kompose.service: test io.kompose.service: test
name: test name: test
namespace: default
spec: spec:
lookupPolicy: lookupPolicy:
local: false local: false