diff --git a/Dockerfile b/Dockerfile
index 8f9cca1..5b13bf5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,6 +3,8 @@ FROM golang:1.19 as builder
ARG TARGETOS
ARG TARGETARCH
+ENV GOPROXY=https://goproxy.cn
+
WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
@@ -28,6 +30,8 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o ma
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /workspace/manager .
+COPY controllers/ controllers/
+
USER 65532:65532
ENTRYPOINT ["/manager"]
diff --git a/Makefile b/Makefile
index 5f9a86c..d3e08a1 100644
--- a/Makefile
+++ b/Makefile
@@ -58,6 +58,10 @@ vet: ## Run go vet against code.
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out
+.PHONY: e2e-test
+e2e-test:
+ cd test/e2e && go test -tags=e2e --config config.yaml -startup-timeout 36000 ./...
+
##@ Build
.PHONY: build
@@ -72,9 +76,13 @@ run: manifests generate fmt vet ## Run a controller from your host.
# (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it.
# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
.PHONY: docker-build
-docker-build: test ## Build docker image with the manager.
+docker-build: ## test ## Build docker image with the manager.
docker build -t ${IMG} .
+.PHONY: docker-load
+docker-load:
+ kind load docker-image ${IMG} --name e2e
+
.PHONY: docker-push
docker-push: ## Push docker image with the manager.
docker push ${IMG}
@@ -153,4 +161,8 @@ $(ENVTEST): $(LOCALBIN)
.PHONY: wait-dep
wait-dep:
- kubectl -n ingress-nginx rollout status deploy/ingress-nginx-controller -w --timeout=40m || kubectl -n ingress-nginx get pod -o wide
\ No newline at end of file
+ kubectl -n ingress-nginx rollout status deploy/ingress-nginx-controller -w --timeout=40m
+
+.PHONY: wait-deploy
+wait-deploy:
+ kubectl -n mashibing-deployment-system rollout status deploy/mashibing-deployment-controller-manager -w --timeout=40m
\ No newline at end of file
diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml
index 687c4a7..a9c4ebd 100644
--- a/config/default/manager_auth_proxy_patch.yaml
+++ b/config/default/manager_auth_proxy_patch.yaml
@@ -31,7 +31,7 @@ spec:
capabilities:
drop:
- "ALL"
- image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0
+ image: localhost:5001/kubebuilder/kube-rbac-proxy:v0.13.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml
index 5c5f0b8..7e20307 100644
--- a/config/manager/kustomization.yaml
+++ b/config/manager/kustomization.yaml
@@ -1,2 +1,8 @@
resources:
- manager.yaml
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+images:
+- name: controller
+ newName: mashibing.com/deployment/msbdeployment
+ newTag: v0.0.1
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index 2bfb619..6c81932 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -5,6 +5,30 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
+- apiGroups:
+ - ""
+ resources:
+ - services
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
- apiGroups:
- apps.mashibing.com
resources:
@@ -31,3 +55,15 @@ rules:
- get
- patch
- update
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - ingresses
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
diff --git a/controllers/msbdeployment_controller.go b/controllers/msbdeployment_controller.go
index c279834..6c69b13 100644
--- a/controllers/msbdeployment_controller.go
+++ b/controllers/msbdeployment_controller.go
@@ -48,6 +48,9 @@ type MsbDeploymentReconciler struct {
//+kubebuilder:rbac:groups=apps.mashibing.com,resources=msbdeployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps.mashibing.com,resources=msbdeployments/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=apps.mashibing.com,resources=msbdeployments/finalizers,verbs=update
+//+kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups="networking.k8s.io",resources=ingresses,verbs=get;list;watch;create;update;patch;delete
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
@@ -287,7 +290,7 @@ func (r *MsbDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&myAppsv1.MsbDeployment{}).
Owns(&appsv1.Deployment{}). // 监控 deployment 类型,变更就触发 reconciler
- Owns(&corev1.Service{}). // 监控 service 类型,变更就触发 reconciler
+ Owns(&corev1.Service{}). // 监控 service 类型,变更就触发 reconciler
Owns(&networkv1.Ingress{}). // 监控 ingress 类型,变更就触发 reconciler
Complete(r)
}
diff --git a/e2e.xml b/e2e.xml
index 5153682..d633718 100644
--- a/e2e.xml
+++ b/e2e.xml
@@ -1,2 +1,2 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/test/e2e/config.yaml b/test/e2e/config.yaml
index 0854ee3..3285d18 100644
--- a/test/e2e/config.yaml
+++ b/test/e2e/config.yaml
@@ -51,3 +51,44 @@ install:
- wait-dep
path: ../..
ignoreFail: false
+ - name: docker-build
+ cmd: make
+ args:
+ - docker-build
+ - IMG=mashibing.com/deployment/msbdeployment:v0.0.1
+ path: ../..
+ ignoreFail: false
+ - name: docker-load
+ cmd: make
+ args:
+ - docker-load
+ - IMG=mashibing.com/deployment/msbdeployment:v0.0.1
+ path: ../..
+ ignoreFail: false
+ - name: install-crd
+ cmd: make
+ args:
+ - install
+ path: ../..
+ ignoreFail: false
+ - name: deploy
+ cmd: make
+ args:
+ - deploy
+ - IMG=mashibing.com/deployment/msbdeployment:v0.0.1
+ path: ../..
+ ignoreFail: false
+ - name: wait-deploy
+ cmd: make
+ args:
+ - wait-deploy
+ path: ../..
+ ignoreFail: false
+ - name: check
+ cmd: kubectl
+ args:
+ - get
+ - pod
+ - -A
+ path: ../..
+ ignoreFail: false
diff --git a/test/e2e/create/create-ingress.go b/test/e2e/create/create-ingress.go
new file mode 100644
index 0000000..006f25b
--- /dev/null
+++ b/test/e2e/create/create-ingress.go
@@ -0,0 +1,78 @@
+package create
+
+import (
+ "context"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/kubernetes"
+ "time"
+
+ "mashibing.com/pkg/mashibing-deployment/test/framework"
+)
+
+// 真正的测试函数
+
+// 测试创建Ingress模式
+func CreateIngressMsbDeployment(ctx *framework.TestContext, f *framework.Framework) {
+ var (
+ // 1. 准备测试数据
+ ctFilePath = "create/testdata/create-ingress.yaml"
+ obj = &unstructured.Unstructured{Object: make(map[string]interface{})}
+ dc dynamic.Interface
+ cs *kubernetes.Clientset
+
+ // 3. 准备测试用到的全局变量
+ msbGVR = schema.GroupVersionResource{
+ Group: "apps.mashibing.com",
+ Version: "v1",
+ Resource: "msbdeployments",
+ }
+ err error
+ )
+ BeforeEach(func() {
+ // 2. 加载测试数据
+ err = f.LoadYamlToUnstructured(ctFilePath, obj)
+ Expect(err).Should(BeNil())
+
+ // 4. 初始化测试用到的全局变量
+ dc = ctx.CreateDynamicClient()
+ cs = ctx.CreateClientSet()
+ })
+ Context("Create msbdeployment mod ingress", func() {
+ It("Should be create mod ingress success", func() {
+ _, err = dc.Resource(msbGVR).Namespace("default").Create(context.TODO(), obj, metav1.CreateOptions{})
+ Expect(err).Should(BeNil())
+
+ By("Sleep 1 second wait creating done")
+ time.Sleep(time.Second)
+ })
+ It("Should be exist msbdeployment", func() {
+ _, err = dc.Resource(msbGVR).Namespace("default").Get(context.TODO(), obj.GetName(), metav1.GetOptions{})
+ Expect(err).Should(BeNil())
+ })
+ It("Should be exist deployment", func() {
+ _, err = cs.AppsV1().Deployments("default").Get(context.TODO(), obj.GetName(), metav1.GetOptions{})
+ Expect(err).Should(BeNil())
+ })
+ It("Should be exist service", func() {
+ _, err = cs.CoreV1().Services("default").Get(context.TODO(), obj.GetName(), metav1.GetOptions{})
+ Expect(err).Should(BeNil())
+ })
+ It("Should be exist ingress", func() {
+ _, err = cs.NetworkingV1().Ingresses("default").Get(context.TODO(), obj.GetName(), metav1.GetOptions{})
+ Expect(err).Should(BeNil())
+ })
+ })
+
+ Context("Delete msbdeployment mod ingress", func() {
+ It("Should be delete mod ingress success", func() {})
+ It("Should not be exist msbdeployment", func() {})
+ It("Should not be exist deployment", func() {})
+ It("Should not be exist service", func() {})
+ It("Should not be exist ingress", func() {})
+ })
+}
diff --git a/test/e2e/create/create-nodeport.go b/test/e2e/create/create-nodeport.go
new file mode 100644
index 0000000..dfb2781
--- /dev/null
+++ b/test/e2e/create/create-nodeport.go
@@ -0,0 +1,28 @@
+package create
+
+import (
+ . "github.com/onsi/ginkgo"
+
+ "mashibing.com/pkg/mashibing-deployment/test/framework"
+)
+
+// 真正的测试函数
+
+// 测试创建Nodeport模式
+func CreateNodeportMsbDeployment(ctx *framework.TestContext, f *framework.Framework) {
+ Context("Create msbdeployment mod nodeport", func() {
+ It("Should be create mode nodeport success", func() {})
+ It("Should be exist msbdeployment", func() {})
+ It("Should be exist deployment", func() {})
+ It("Should be exist service", func() {})
+ It("Should not be exist ingress", func() {})
+ })
+
+ Context("Delete msbdeployment mod nodeport", func() {
+ It("Should be delete mod nodeport success", func() {})
+ It("Should not be exist msbdeployment", func() {})
+ It("Should not be exist deployment", func() {})
+ It("Should not be exist service", func() {})
+ It("Should not be exist ingress", func() {})
+ })
+}
diff --git a/test/e2e/create/testdata/create-ingress.yaml b/test/e2e/create/testdata/create-ingress.yaml
new file mode 100644
index 0000000..1b986d0
--- /dev/null
+++ b/test/e2e/create/testdata/create-ingress.yaml
@@ -0,0 +1,11 @@
+apiVersion: apps.mashibing.com/v1
+kind: MsbDeployment
+metadata:
+ name: create-ingress
+spec:
+ image: nginx
+ port: 80
+ replicas: 2
+ expose:
+ mode: ingress
+ ingressDomain: www.mashingbing-test.com
\ No newline at end of file
diff --git a/test/e2e/create/testdata/create-nodeport.yaml b/test/e2e/create/testdata/create-nodeport.yaml
new file mode 100644
index 0000000..9dced4f
--- /dev/null
+++ b/test/e2e/create/testdata/create-nodeport.yaml
@@ -0,0 +1,11 @@
+apiVersion: apps.mashibing.com/v1
+kind: MsbDeployment
+metadata:
+ name: create-nodeport
+spec:
+ image: nginx
+ port: 80
+ replicas: 2
+ expose:
+ mode: nodeport
+ nodePort: 30000
\ No newline at end of file
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
new file mode 100644
index 0000000..cc01ffa
--- /dev/null
+++ b/test/e2e/create_test.go
@@ -0,0 +1,9 @@
+//go:build e2e
+// +build e2e
+
+package e2e
+
+import "mashibing.com/pkg/mashibing-deployment/test/e2e/create"
+
+var _ = fmw.Describe("Create msbdeployment mod ingress", create.CreateIngressMsbDeployment)
+var _ = fmw.Describe("Create msbdeployment mod nodeport", create.CreateNodeportMsbDeployment)
diff --git a/test/e2e/e2e.xml b/test/e2e/e2e.xml
index 83fc8b4..74a809a 100644
--- a/test/e2e/e2e.xml
+++ b/test/e2e/e2e.xml
@@ -1,2 +1,47 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/e2e/update/testdata/update-i2n.yaml b/test/e2e/update/testdata/update-i2n.yaml
new file mode 100644
index 0000000..868bd1c
--- /dev/null
+++ b/test/e2e/update/testdata/update-i2n.yaml
@@ -0,0 +1,11 @@
+apiVersion: apps.mashibing.com/v1
+kind: MsbDeployment
+metadata:
+ name: update-ingress
+spec:
+ image: nginx
+ port: 80
+ replicas: 2
+ expose:
+ mode: nodeport
+ nodePort: 30000
\ No newline at end of file
diff --git a/test/e2e/update/testdata/update-ingress.yaml b/test/e2e/update/testdata/update-ingress.yaml
new file mode 100644
index 0000000..b8b70bd
--- /dev/null
+++ b/test/e2e/update/testdata/update-ingress.yaml
@@ -0,0 +1,11 @@
+apiVersion: apps.mashibing.com/v1
+kind: MsbDeployment
+metadata:
+ name: update-ingress
+spec:
+ image: nginx
+ port: 80
+ replicas: 2
+ expose:
+ mode: ingress
+ ingressDomain: www.mashingbing-test.com
\ No newline at end of file
diff --git a/test/e2e/update/testdata/update-n2i.yaml b/test/e2e/update/testdata/update-n2i.yaml
new file mode 100644
index 0000000..76c89c8
--- /dev/null
+++ b/test/e2e/update/testdata/update-n2i.yaml
@@ -0,0 +1,11 @@
+apiVersion: apps.mashibing.com/v1
+kind: MsbDeployment
+metadata:
+ name: update-nodeport
+spec:
+ image: nginx
+ port: 80
+ replicas: 2
+ expose:
+ mode: ingress
+ ingressDomain: www.mashingbing-test.com
diff --git a/test/e2e/update/testdata/update-nodeport.yaml b/test/e2e/update/testdata/update-nodeport.yaml
new file mode 100644
index 0000000..92db521
--- /dev/null
+++ b/test/e2e/update/testdata/update-nodeport.yaml
@@ -0,0 +1,11 @@
+apiVersion: apps.mashibing.com/v1
+kind: MsbDeployment
+metadata:
+ name: update-nodeport
+spec:
+ image: nginx
+ port: 80
+ replicas: 2
+ expose:
+ mode: nodeport
+ nodePort: 30000
\ No newline at end of file
diff --git a/test/e2e/update/update-i2n.go b/test/e2e/update/update-i2n.go
new file mode 100644
index 0000000..2fad3ad
--- /dev/null
+++ b/test/e2e/update/update-i2n.go
@@ -0,0 +1,31 @@
+package update
+
+import (
+ . "github.com/onsi/ginkgo"
+
+ "mashibing.com/pkg/mashibing-deployment/test/framework"
+)
+
+// 真正的测试函数
+
+// 测试从Ingress模式更新为Nodeport模式
+func UpdateI2NMsbDeployment(ctx *framework.TestContext, f *framework.Framework) {
+ Context("Update msbdeployment mod ingress to nodeport", func() {
+ It("Should be create mod ingress success", func() {})
+ It("Should be exist msbdeployment", func() {})
+ It("Should be exist deployment", func() {})
+ It("Should be exist service", func() {})
+ It("Should be exist ingress", func() {})
+
+ It("Should be update to nodeport success", func() {})
+ It("Should not be exist ingress", func() {})
+ })
+
+ Context("Delete msbdeployment i2n", func() {
+ It("Should be delete success", func() {})
+ It("Should not be exist msbdeployment", func() {})
+ It("Should not be exist deployment", func() {})
+ It("Should not be exist service", func() {})
+ It("Should not be exist ingress", func() {})
+ })
+}
diff --git a/test/e2e/update/update-n2i.go b/test/e2e/update/update-n2i.go
new file mode 100644
index 0000000..010a2cc
--- /dev/null
+++ b/test/e2e/update/update-n2i.go
@@ -0,0 +1,31 @@
+package update
+
+import (
+ . "github.com/onsi/ginkgo"
+
+ "mashibing.com/pkg/mashibing-deployment/test/framework"
+)
+
+// 真正的测试函数
+
+// 测试从Nodeport模式更新为Ingress模式
+func UpdateN2IMsbDeployment(ctx *framework.TestContext, f *framework.Framework) {
+ Context("Update msbdeployment mod nodeport to ingress", func() {
+ It("Should be create mod nodeport success", func() {})
+ It("Should be exist msbdeployment", func() {})
+ It("Should be exist deployment", func() {})
+ It("Should be exist service", func() {})
+ It("Should not be exist ingress", func() {})
+
+ It("Should be update to ingress success", func() {})
+ It("Should be exist ingress", func() {})
+ })
+
+ Context("Delete msbdeployment n2i", func() {
+ It("Should be delete success", func() {})
+ It("Should not be exist msbdeployment", func() {})
+ It("Should not be exist deployment", func() {})
+ It("Should not be exist service", func() {})
+ It("Should not be exist ingress", func() {})
+ })
+}
diff --git a/test/e2e/update_test.go b/test/e2e/update_test.go
new file mode 100644
index 0000000..200ab4f
--- /dev/null
+++ b/test/e2e/update_test.go
@@ -0,0 +1,9 @@
+//go:build e2e
+// +build e2e
+
+package e2e
+
+import "mashibing.com/pkg/mashibing-deployment/test/e2e/update"
+
+var _ = fmw.Describe("Update msbdeployment mod ingress to Nodeport", update.UpdateI2NMsbDeployment)
+var _ = fmw.Describe("Update msbdeployment mod nodeport to Ingress", update.UpdateN2IMsbDeployment)
diff --git a/test/framework/framework.go b/test/framework/framework.go
index edfe4e4..8d52e84 100644
--- a/test/framework/framework.go
+++ b/test/framework/framework.go
@@ -5,12 +5,6 @@ import (
"flag"
"fmt"
"io"
- corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/validation/field"
- "k8s.io/client-go/kubernetes"
- "k8s.io/client-go/rest"
"math/rand"
"os"
"regexp"
@@ -21,6 +15,14 @@ import (
"github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/reporters"
"github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/util/validation/field"
+ "k8s.io/apimachinery/pkg/util/yaml"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/rest"
)
var DefaultStartTimeout = float64(60 * 60)
@@ -199,6 +201,17 @@ func (f *Framework) DestroyTestEnvironment() error {
}
+// 加载测试文件内容到对象中
+func (f *Framework) LoadYamlToUnstructured(ctFilePath string, obj *unstructured.Unstructured) error {
+ data, err := os.ReadFile(ctFilePath)
+ if err != nil {
+ return err
+ }
+ //var o = map[string]interface{}{}
+ //yaml.Unmarshal(data, &o)
+ return yaml.Unmarshal(data, &(obj.Object))
+}
+
func (f *Framework) Describe(name string, ctxFunc ContextFunc) bool {
// 整个函数,实际上是调用 ginkgo的Describe
return ginkgo.Describe(name, func() {
@@ -235,10 +248,7 @@ func (f *Framework) createTestContext(name string, nsCreate bool) (TestContext,
tc := TestContext{}
// 2. 检查 f 是否为空
if f.Config == nil || f.ClusterConfig == nil {
- return tc, field.Invalid(
- field.NewPath("config/clusterConfig"),
- nil,
- "Not inital config object")
+ return tc, nil
}
// 3. 填充字段
@@ -300,5 +310,4 @@ func (f *Framework) deleteTestContext(ctx TestContext) error {
// 如果执行过程中出现错误,同样使用errs = append(errs, err)来追加错误
return errs.ToAggregate()
-
}
diff --git a/test/framework/test-context.go b/test/framework/test-context.go
index 675f736..8a2ca2c 100644
--- a/test/framework/test-context.go
+++ b/test/framework/test-context.go
@@ -1,6 +1,12 @@
package framework
-import "k8s.io/client-go/rest"
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/rest"
+)
// 1. 定义一个测试的入口函数Describe,这里接收测试的描述以及contextFunc
// 1.1 这里边会调用context创建方法来创建context
@@ -17,3 +23,27 @@ type TestContext struct {
}
type ContextFunc func(ctx *TestContext, f *Framework)
+
+// 如果不用动态的client,那么我们访问这些资源的时候,就需要:
+// 1. 自己创建rest api 的请求
+// 2. 获取对应资源的 client sdk
+
+// CreateDynamicClient 创建动态的 client,用来访问自定定义或者后安装的资源
+func (tc *TestContext) CreateDynamicClient() dynamic.Interface {
+ By("Create a Dynamic Client")
+ c, err := dynamic.NewForConfig(tc.Config)
+ if err != nil {
+ Expect(err).Should(BeNil())
+ }
+ return c
+}
+
+// 创建一个 clientset,用来访问内置资源
+func (tc *TestContext) CreateClientSet() *kubernetes.Clientset {
+ By("Create a ClientSet client")
+ c, err := kubernetes.NewForConfig(tc.Config)
+ if err != nil {
+ Expect(err).Should(BeNil())
+ }
+ return c
+}