Skip to content

Commit

Permalink
Add kclient functions for creating deployments
Browse files Browse the repository at this point in the history
Signed-off-by: Rajiv Senthilnathan <rajivsen@ca.ibm.com>
  • Loading branch information
Rajiv Senthilnathan committed Jan 13, 2020
1 parent 7647d90 commit 3a77406
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 0 deletions.
37 changes: 37 additions & 0 deletions pkg/kclient/deployments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package kclient

import (
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// CreateDeployment creates a deployment based on the given pod
func (c *Client) CreateDeployment(pod *corev1.Pod) (*appsv1.Deployment, error) {

replicas := int32(1)
deployment := appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: pod.ObjectMeta,
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: pod.Labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: pod.ObjectMeta,
Spec: pod.Spec,
},
},
}

deploy, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Create(&deployment)
if err != nil {
return nil, errors.Wrapf(err, "unable to create Deployment for %s", pod.ObjectMeta.Name)
}
return deploy, nil
}
106 changes: 106 additions & 0 deletions pkg/kclient/deployments_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package kclient

import (
"testing"

"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"

ktesting "k8s.io/client-go/testing"
)

func TestCreateDeployment(t *testing.T) {

container := &corev1.Container{
Name: "container1",
Image: "image1",
ImagePullPolicy: corev1.PullAlways,

Command: []string{"tail"},
Args: []string{"-f", "/dev/null"},
Env: []corev1.EnvVar{},
}

pod := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "",
Namespace: "default",
Labels: map[string]string{
"app": "app",
"component": "frontend",
},
},
Spec: corev1.PodSpec{
ServiceAccountName: "default",
Containers: []corev1.Container{*container},
},
}

tests := []struct {
name string
deploymentName string
wantErr bool
}{
{
name: "Case: Valid deployment name",
deploymentName: "pod",
wantErr: false,
},
{
name: "Case: Invalid deployment name",
deploymentName: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// initialising the fakeclient
fkclient, fkclientset := FakeNew()
fkclient.Namespace = "default"

fkclientset.Kubernetes.PrependReactor("create", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
if tt.deploymentName == "" {
return true, nil, errors.Errorf("deployment name is empty")
}
deployment := appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: tt.deploymentName,
},
}
return true, &deployment, nil
})

pod.ObjectMeta.Name = tt.deploymentName
createdDeployment, err := fkclient.CreateDeployment(pod)

// Checks for unexpected error cases
if !tt.wantErr == (err != nil) {
t.Errorf("fkclient.CreateDeployment(pod) unexpected error %v, wantErr %v", err, tt.wantErr)
}

if err == nil {

if len(fkclientset.Kubernetes.Actions()) != 1 {
t.Errorf("expected 1 action in StartDeployment got: %v", fkclientset.Kubernetes.Actions())
} else {
if createdDeployment.Name != tt.deploymentName {
t.Errorf("deployment name does not match the expected name, expected: %s, got %s", tt.deploymentName, createdDeployment.Name)
}
}

}

})
}
}
50 changes: 50 additions & 0 deletions pkg/kclient/generators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package kclient

import (

// api resource types
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// GenerateContainerSpec creates a container spec that can be used when creating a pod
func GenerateContainerSpec(name, image string, isPrivileged bool, command, args []string, envVars []corev1.EnvVar) corev1.Container {
container := &corev1.Container{
Name: name,
Image: image,
ImagePullPolicy: corev1.PullAlways,

Command: command,
Args: args,
Env: envVars,
}

if isPrivileged {
container.SecurityContext = &corev1.SecurityContext{
Privileged: &isPrivileged,
}
}

return *container
}

// GeneratePodSpec creates a pod spec
func GeneratePodSpec(podName, namespace, serviceAccountName string, labels map[string]string, containers []corev1.Container) *corev1.Pod {
pod := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Namespace: namespace,
Labels: labels,
},
Spec: corev1.PodSpec{
ServiceAccountName: serviceAccountName,
Containers: containers,
},
}

return pod
}
154 changes: 154 additions & 0 deletions pkg/kclient/generators_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package kclient

import (
"testing"

corev1 "k8s.io/api/core/v1"
)

func TestGenerateContainerSpec(t *testing.T) {

tests := []struct {
name string
image string
isPrivileged bool
command []string
args []string
envVars []corev1.EnvVar
}{
{
name: "",
image: "",
isPrivileged: false,
command: []string{},
args: []string{},
envVars: []corev1.EnvVar{},
},
{
name: "container1",
image: "quay.io/eclipse/che-java8-maven:nightly",
isPrivileged: true,
command: []string{"tail"},
args: []string{"-f", "/dev/null"},
envVars: []corev1.EnvVar{
{
Name: "test",
Value: "123",
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

containerSpec := GenerateContainerSpec(tt.name, tt.image, tt.isPrivileged, tt.command, tt.args, tt.envVars)

if containerSpec.Name != tt.name {
t.Errorf("expected %s, actual %s", tt.name, containerSpec.Name)
}

if containerSpec.Image != tt.image {
t.Errorf("expected %s, actual %s", tt.image, containerSpec.Image)
}

if tt.isPrivileged {
if *containerSpec.SecurityContext.Privileged != tt.isPrivileged {
t.Errorf("expected %t, actual %t", tt.isPrivileged, *containerSpec.SecurityContext.Privileged)
}
} else if tt.isPrivileged == false && containerSpec.SecurityContext != nil {
t.Errorf("expected security context to be nil but it was defined")
}

if len(containerSpec.Command) != len(tt.command) {
t.Errorf("expected %d, actual %d", len(tt.command), len(containerSpec.Command))
} else {
for i := range containerSpec.Command {
if containerSpec.Command[i] != tt.command[i] {
t.Errorf("expected %s, actual %s", tt.command[i], containerSpec.Command[i])
}
}
}

if len(containerSpec.Args) != len(tt.args) {
t.Errorf("expected %d, actual %d", len(tt.args), len(containerSpec.Args))
} else {
for i := range containerSpec.Args {
if containerSpec.Args[i] != tt.args[i] {
t.Errorf("expected %s, actual %s", tt.args[i], containerSpec.Args[i])
}
}
}

if len(containerSpec.Env) != len(tt.envVars) {
t.Errorf("expected %d, actual %d", len(tt.envVars), len(containerSpec.Env))
} else {
for i := range containerSpec.Env {
if containerSpec.Env[i].Name != tt.envVars[i].Name {
t.Errorf("expected name %s, actual name %s", tt.envVars[i].Name, containerSpec.Env[i].Name)
}
if containerSpec.Env[i].Value != tt.envVars[i].Value {
t.Errorf("expected value %s, actual value %s", tt.envVars[i].Value, containerSpec.Env[i].Value)
}
}
}

})
}
}

func TestGeneratePodSpec(t *testing.T) {

container := &corev1.Container{
Name: "container1",
Image: "image1",
ImagePullPolicy: corev1.PullAlways,

Command: []string{"tail"},
Args: []string{"-f", "/dev/null"},
Env: []corev1.EnvVar{},
}

tests := []struct {
podName string
namespace string
serviceAccount string
labels map[string]string
}{
{
podName: "podSpecTest",
namespace: "default",
serviceAccount: "default",
labels: map[string]string{
"app": "app",
"component": "frontend",
},
},
}

for _, tt := range tests {
t.Run(tt.podName, func(t *testing.T) {

podSpec := GeneratePodSpec(tt.podName, tt.namespace, tt.serviceAccount, tt.labels, []corev1.Container{*container})

if podSpec.Name != tt.podName {
t.Errorf("expected %s, actual %s", tt.podName, podSpec.Name)
}

if podSpec.Namespace != tt.namespace {
t.Errorf("expected %s, actual %s", tt.namespace, podSpec.Namespace)
}

if len(podSpec.Labels) != len(tt.labels) {
t.Errorf("expected %d, actual %d", len(tt.labels), len(podSpec.Labels))
} else {
for i := range podSpec.Labels {
if podSpec.Labels[i] != tt.labels[i] {
t.Errorf("expected %s, actual %s", tt.labels[i], podSpec.Labels[i])
}
}
}

})
}
}

0 comments on commit 3a77406

Please sign in to comment.