Skip to content

Commit

Permalink
[WIP] CRUD operations for managing cluster objects
Browse files Browse the repository at this point in the history
  • Loading branch information
ShwethaKumbla committed Jun 29, 2021
1 parent 3736def commit 3a58e73
Show file tree
Hide file tree
Showing 8 changed files with 947 additions and 21 deletions.
7 changes: 5 additions & 2 deletions go.mod
Expand Up @@ -3,6 +3,9 @@ module sigs.k8s.io/e2e-framework
go 1.16

require (
k8s.io/apimachinery v0.21.0
k8s.io/client-go v0.21.0
github.com/vladimirvivien/gexe v0.1.0
k8s.io/api v0.21.1
k8s.io/apimachinery v0.21.1
k8s.io/client-go v0.21.1
sigs.k8s.io/controller-runtime v0.9.0
)
330 changes: 311 additions & 19 deletions go.sum

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions klient/k8s/object.go
@@ -0,0 +1,37 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package k8s

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

// Object is a union type that can represent either typed objects
// of type metav1.Object or dynamic objects of type runtime.object.
type Object interface {
metav1.Object
runtime.Object
}

// ObjectList is a Kubernetes object list, allows functions to work
// with any resource that implements both runtime.Object and
// metav1.ListInterface interfaces.
type ObjectList interface {
metav1.ListInterface
runtime.Object
}
162 changes: 162 additions & 0 deletions klient/k8s/res/main_test.go
@@ -0,0 +1,162 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package res

import (
"context"
"flag"
"fmt"
"os"
"path/filepath"
"testing"
"time"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/util/homedir"
"sigs.k8s.io/e2e-framework/klient/conf"
test "sigs.k8s.io/e2e-framework/klient/k8s/testing"
)

var (
kubeconfig string
dep *appsv1.Deployment
clientset *kubernetes.Clientset
count uint64
replicaCount int32 = 2
ctx = context.TODO()
cfg *rest.Config
namespace *corev1.Namespace
kind *test.KindCluster
)

func TestMain(m *testing.M) {
setup()
initializeResObjects()
code := m.Run()
teardown()
os.Exit(code)
}

func setup() {
home := homedir.HomeDir()
path := filepath.Join(home, ".kube", "config")

// setup a kind cluster
err := setupKindCluster()
if err != nil {
fmt.Println("error while setting up kind cluster", err)
return
}

flag.StringVar(&kubeconfig, "kubeconfig", "", "Paths to a kubeconfig. Only required if out-of-cluster.")

// set --kubeconfig flag
err = flag.Set("kubeconfig", path)
if err != nil {
fmt.Println("unexpected error while setting flag value", err)
return
}

flag.Parse()

cfg, err = conf.New(conf.ResolveKubeConfigFile())
if err != nil {
fmt.Println("error while client connection", err)
}

clientset, err = kubernetes.NewForConfig(cfg)
if err != nil {
fmt.Println("error while client set connection", err)
}
}

// setupKindCluster
func setupKindCluster() error {
// yamlPath, err := filepath.Abs(filepath.Join("..", "./testing", "/kind-cluster-docker.yaml"))
// if err != nil {
// return err
// }

kind = test.NewKindCluster("e2e-test-cluster")
if err := kind.Create(); err != nil {
return err
}
fmt.Println("kind cluster created")

// stall to wait for kind pods initialization
waitTime := time.Second * 10
fmt.Println("waiting for kind pods to initialize...", waitTime)
time.Sleep(waitTime)

return nil
}

func teardown() {
deleteDeployment(ctx, dep, namespace.Name)
deleteNamespace(ctx, namespace)

// delete kind cluster
err := kind.Destroy()
if err != nil {
fmt.Println("error while deleting the cluster", err)
return
}
}

func deleteDeployment(ctx context.Context, dep *appsv1.Deployment, ns string) {
_, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
if err == nil {
err = clientset.AppsV1().Deployments(ns).Delete(ctx, dep.Name, metav1.DeleteOptions{})
if err != nil {
fmt.Println("error while deleting deployment", err)
}
}
}

func deleteNamespace(ctx context.Context, ns *corev1.Namespace) {
ns, err := clientset.CoreV1().Namespaces().Get(ctx, ns.Name, metav1.GetOptions{})
if err != nil {
return
}

err = clientset.CoreV1().Namespaces().Delete(ctx, ns.Name, metav1.DeleteOptions{})
if err != nil {
fmt.Println("error while deleting namespace", err)
}
}

func initializeResObjects() {
namespace = &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}

dep = &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("deployment-name-%v", count), Namespace: namespace.Name, Labels: map[string]string{"app": fmt.Sprintf("bar-%v", count)}},
Spec: appsv1.DeploymentSpec{
Replicas: &replicaCount,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
},
},
}
}
122 changes: 122 additions & 0 deletions klient/k8s/res/resource.go
@@ -0,0 +1,122 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package res

import (
"context"
"errors"
"fmt"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
cr "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/e2e-framework/klient/k8s"
)

type Resources struct {
// config is the rest.Config to talk to an apiserver
config *rest.Config

// scheme will be used to map go structs to GroupVersionKinds
scheme *runtime.Scheme

// client is a wrapper for controller runtime client
client cr.Client
}

// Res instantiates the controller runtime client
// object. User can get panic for belopw scenarios.
// 1. if user does not provide k8s config
// 2. if controller runtime client instantiation fails.
func Res(cfg *rest.Config) *Resources {
if cfg == nil {
// TODO: logging
fmt.Println("must provide rest.Config")
panic(errors.New("must provide rest.Config"))
}

cl, err := cr.New(cfg, cr.Options{Scheme: scheme.Scheme})
if err != nil {
// TODO: log error
panic(err)
}

res := &Resources{
config: cfg,
scheme: scheme.Scheme,
client: cl,
}

return res
}

func (r *Resources) Get(ctx context.Context, name, namespace string, obj k8s.Object) error {
return r.client.Get(ctx, cr.ObjectKey{Namespace: namespace, Name: name}, obj)
}

type CreateOption func(*metav1.CreateOptions)

func (r *Resources) Create(ctx context.Context, obj k8s.Object, opts ...CreateOption) error {
createOptions := &metav1.CreateOptions{}
for _, fn := range opts {
fn(createOptions)
}

o := &cr.CreateOptions{Raw: createOptions}

return r.client.Create(ctx, obj, o)
}

type UpdateOption func(*metav1.UpdateOptions)

func (r *Resources) Update(ctx context.Context, obj k8s.Object, opts ...UpdateOption) error {
updateOptions := &metav1.UpdateOptions{}
for _, fn := range opts {
fn(updateOptions)
}

o := &cr.UpdateOptions{Raw: updateOptions}
return r.client.Update(ctx, obj, o)
}

type DeleteOption func(*metav1.DeleteOptions)

func (r *Resources) Delete(ctx context.Context, obj k8s.Object, opts ...DeleteOption) error {
deleteOptions := &metav1.DeleteOptions{}
for _, fn := range opts {
fn(deleteOptions)
}

o := &cr.DeleteOptions{Raw: deleteOptions}
return r.client.Delete(ctx, obj, o)
}

type ListOption func(*metav1.ListOptions)

func (r *Resources) List(ctx context.Context, objs k8s.ObjectList, opts ...ListOption) error {
listOptions := &metav1.ListOptions{}

for _, fn := range opts {
fn(listOptions)
}

o := &cr.ListOptions{Raw: listOptions}
return r.client.List(ctx, objs, o)
}

0 comments on commit 3a58e73

Please sign in to comment.