generated from kubernetes/kubernetes-template-project
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] CRUD operations for managing cluster objects
- Loading branch information
1 parent
3736def
commit 3a58e73
Showing
8 changed files
with
947 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"}}}, | ||
}, | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
Oops, something went wrong.