You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by ch...@apache.org on 2022/07/21 14:17:43 UTC
[submarine] branch master updated: SUBMARINE-1292. Add submarine-cloud-v3 e2e test
This is an automated email from the ASF dual-hosted git repository.
chishengliu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git
The following commit(s) were added to refs/heads/master by this push:
new e4df9741 SUBMARINE-1292. Add submarine-cloud-v3 e2e test
e4df9741 is described below
commit e4df9741a547dd8aae3058863f35529c88fa62ca
Author: joshvictor1024 <jo...@gmail.com>
AuthorDate: Thu Jul 14 09:09:31 2022 +0800
SUBMARINE-1292. Add submarine-cloud-v3 e2e test
### What is this PR for?
<!-- A few sentences describing the overall goals of the pull request's commits.
First time? Check out the contributing guide - https://submarine.apache.org/contribution/contributions.html
-->
Add end-to-end test to submarine-cloud-v3
### What type of PR is it?
Feature
### Todos
* [x] - prepare to run in-cluster
* [x] - write e2e test and add to Github Action
* [x] - add new development doc
### What is the Jira issue?
<!-- * Open an issue on Jira https://issues.apache.org/jira/browse/SUBMARINE/
* Put link here, and add [SUBMARINE-*Jira number*] in PR title, eg. `SUBMARINE-23. PR title`
-->
https://issues.apache.org/jira/browse/SUBMARINE-1292
### How should this be tested?
<!--
* First time? Setup Travis CI as described on https://submarine.apache.org/contribution/contributions.html#continuous-integration
* Strongly recommended: add automated unit tests for any new or changed behavior
* Outline any manual steps to test the PR here.
-->
Follow `submarine-cloud-v3/docs/developer-guide.md`
### Screenshots (if appropriate)
![1292](https://user-images.githubusercontent.com/55046554/178652318-39ef632b-a36d-48c6-b416-dddb00026998.png)
### Questions:
* Do the license files need updating? No
* Are there breaking changes for older versions? No
* Does this need new documentation? Yes
Author: joshvictor1024 <jo...@gmail.com>
Signed-off-by: Chi-Sheng Liu <ch...@apache.org>
Closes #978 from joshvictor1024/SUBMARINE-1292 and squashes the following commits:
eba20cab [joshvictor1024] change operator image user and group name
35a7c9de [joshvictor1024] add e2e to GA
8bda1ef3 [joshvictor1024] add e2e test
ad0eea60 [joshvictor1024] prepare to run in-cluster
5bd50968 [joshvictor1024] add rbac rules
ae4e3f6f [joshvictor1024] fix no regular resync
---
.../scripts/build-image-locally-v3.sh | 41 ++--
.github/workflows/master.yml | 91 +++++++++
.../docker-images/operator-v3}/Dockerfile | 47 ++---
dev-support/docker-images/operator-v3/build.sh | 38 ++++
submarine-cloud-v3/Dockerfile | 26 ++-
submarine-cloud-v3/Makefile | 2 +-
.../artifacts/submarine-virtualservice.yaml | 2 +-
.../config/default/kustomization.yaml | 23 ++-
.../patches/psp-clustertype-kubernetes-patch.yaml} | 38 ++--
.../patches/psp-clustertype-openshift-patch.yaml} | 38 ++--
submarine-cloud-v3/config/manager/manager.yaml | 7 +-
submarine-cloud-v3/config/rbac/role.yaml | 97 +++-------
.../controllers/submarine_controller.go | 30 +--
.../controllers/submarine_controller_test.go | 210 +++++++++++++++++++++
submarine-cloud-v3/controllers/suite_test.go | 32 +++-
submarine-cloud-v3/docs/developer-guide.md | 58 ++++--
16 files changed, 568 insertions(+), 212 deletions(-)
diff --git a/submarine-cloud-v3/Dockerfile b/.github/scripts/build-image-locally-v3.sh
old mode 100644
new mode 100755
similarity index 50%
copy from submarine-cloud-v3/Dockerfile
copy to .github/scripts/build-image-locally-v3.sh
index 7c009d21..81203af7
--- a/submarine-cloud-v3/Dockerfile
+++ b/.github/scripts/build-image-locally-v3.sh
@@ -1,3 +1,4 @@
+#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@@ -15,30 +16,20 @@
# limitations under the License.
#
-# Build the manager binary
-FROM golang:1.17 as builder
+SUBMARINE_VERSION="0.8.0-SNAPSHOT"
+FOLDER_LIST=("database" "mlflow" "submarine" "operator-v3")
+IMAGE_LIST=(
+ "apache/submarine:database-${SUBMARINE_VERSION}"
+ "apache/submarine:mlflow-${SUBMARINE_VERSION}"
+ "apache/submarine:server-${SUBMARINE_VERSION}"
+ "apache/submarine:operator-${SUBMARINE_VERSION}"
+)
-WORKDIR /workspace
-# Copy the Go Modules manifests
-COPY go.mod go.mod
-COPY go.sum go.sum
-# cache deps before building and copying source so that we don't need to re-download as much
-# and so that source changes don't invalidate our downloaded layer
-RUN go mod download
+for i in "${!IMAGE_LIST[@]}"
+do
+ echo "Build Image ${IMAGE_LIST[i]}"
+ echo "Execute ./dev-support/docker-images/${FOLDER_LIST[i]}/build.sh"
+ ./dev-support/docker-images/"${FOLDER_LIST[i]}"/build.sh
+ kind load docker-image "${IMAGE_LIST[i]}"
+done
-# Copy the go source
-COPY main.go main.go
-COPY api/ api/
-COPY controllers/ controllers/
-
-# Build
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
-
-# Use distroless as minimal base image to package the manager binary
-# Refer to https://github.com/GoogleContainerTools/distroless for more details
-FROM gcr.io/distroless/static:nonroot
-WORKDIR /
-COPY --from=builder /workspace/manager .
-USER 65532:65532
-
-ENTRYPOINT ["/manager"]
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index dcaafecc..9d74e5b4 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -132,6 +132,97 @@ jobs:
#Never cache local artifacts
rm -rf ~/.m2/repository/org/apache/submarine
if: always()
+ submarine-operator-v3-e2e-test:
+ needs: generate-k8s-versions-array
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ strategy:
+ matrix:
+ k8s-version: ${{fromJSON(needs.generate-k8s-versions-array.outputs.matrix)}}
+ fail-fast: false
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 50
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: "1.8"
+ - name: Set up Maven 3.6.3
+ uses: stCarolas/setup-maven@v4
+ with:
+ maven-version: 3.6.3
+ - name: Setup Golang 1.17.2
+ uses: actions/setup-go@v2
+ with:
+ go-version: "1.17.2"
+ - uses: actions/cache@v2
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+ - name: Check version
+ run: |
+ mvn --version
+ java -version
+ go version
+ helm version
+ kind version
+ - name: Create kind cluster
+ run: kind create cluster --config ./.github/config/kind-config-kind.yaml --wait 3m --image kindest/node:${{ matrix.k8s-version }}
+ - name: Download Istio 1.13.0
+ run: |
+ curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.13.0 sh -
+ cd istio-1.13.0
+ echo "$PWD/bin" >> $GITHUB_PATH
+ - name: Install Istio
+ run: istioctl install -y --set values.global.proxy.resources.requests.cpu=10m
+ - name: Show K8s cluster information
+ run: |
+ kubectl cluster-info
+ kubectl version
+ kubectl get pods -n kube-system
+ echo "current-context:" $(kubectl config current-context)
+ echo "environment-kubeconfig:" ${KUBECONFIG}
+ - name: Build Image locally
+ run: .github/scripts/build-image-locally-v3.sh
+ - name: Install Golang Dependencies
+ working-directory: submarine-cloud-v3
+ run: go mod vendor
+ - name: Install Submarine operator
+ working-directory: submarine-cloud-v3
+ # run: make deploy
+ # GA env variable VERSION overrides the variable in makefile,
+ # which results in the wrong image name.
+ # Running commands manually instead.
+ run: |
+ cd config/manager
+ kustomize edit set image controller=apache/submarine:operator-0.8.0-SNAPSHOT
+ kustomize build ../default | kubectl apply -f -
+ - name: Install Helm Dependencies
+ run: |
+ sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/
+ sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld
+ helm install --wait --set dev=true --set storageClass.provisioner=rancher.io/local-path --set storageClass.volumeBindingMode=WaitForFirstConsumer submarine ./helm-charts/submarine -n submarine-cloud-v3-system
+ - name: Run end-to-end test
+ working-directory: submarine-cloud-v3
+ run: go test ./controllers/ -v -ginkgo.v
+ - name: Failure status
+ run: |
+ kubectl describe nodes
+ kubectl get all -A
+ kubectl get events -A --sort-by='{.lastTimestamp}'
+ kubectl logs -l control-plane=controller-manager -n submarine-cloud-v3-system
+ kubectl logs -l app=notebook-controller -n submarine-cloud-v3-system
+ kubectl logs -l control-plane=kubeflow-training-operator -n submarine-cloud-v3-system
+ kubectl describe submarine -A
+ if: ${{ failure() }}
+ - name: Delete temporary build artifacts before caching
+ run: |
+ #Never cache local artifacts
+ rm -rf ~/.m2/repository/org/apache/submarine
+ if: always()
submarine-e2e:
runs-on: ubuntu-latest
timeout-minutes: 60
diff --git a/submarine-cloud-v3/Dockerfile b/dev-support/docker-images/operator-v3/Dockerfile
similarity index 54%
copy from submarine-cloud-v3/Dockerfile
copy to dev-support/docker-images/operator-v3/Dockerfile
index 7c009d21..87f12256 100644
--- a/submarine-cloud-v3/Dockerfile
+++ b/dev-support/docker-images/operator-v3/Dockerfile
@@ -15,30 +15,31 @@
# limitations under the License.
#
+# Adapted from submarine-cloud-v3/Dockerfile and dev-support/docker-images/operator/Dockerfile
+
# Build the manager binary
-FROM golang:1.17 as builder
-
-WORKDIR /workspace
-# Copy the Go Modules manifests
-COPY go.mod go.mod
-COPY go.sum go.sum
-# cache deps before building and copying source so that we don't need to re-download as much
-# and so that source changes don't invalidate our downloaded layer
-RUN go mod download
-
-# Copy the go source
-COPY main.go main.go
-COPY api/ api/
-COPY controllers/ controllers/
-
-# Build
+FROM golang:1.17.2 as build-image
+MAINTAINER Apache Software Foundation <de...@submarine.apache.org>
+
+ADD tmp/submarine-cloud-v3 /usr/src
+
+WORKDIR /usr/src
+
+# use CGO_ENABLED=0 to support alpine image
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
-# Use distroless as minimal base image to package the manager binary
-# Refer to https://github.com/GoogleContainerTools/distroless for more details
-FROM gcr.io/distroless/static:nonroot
-WORKDIR /
-COPY --from=builder /workspace/manager .
-USER 65532:65532
+# use alpine to support shell params
+FROM alpine
+MAINTAINER Apache Software Foundation <de...@submarine.apache.org>
+
+WORKDIR /usr/src
+COPY tmp/submarine-cloud-v3/artifacts/ /usr/src/artifacts/
+COPY --from=build-image /usr/src/manager /usr/src/manager
+
+# manager is run as non-root user
+# See submarine-cloud-v3/config/manager/manager.yaml
+RUN addgroup -g 1000 -S submarine
+RUN adduser -u 1000 -S submarine -G submarine
+USER submarine
-ENTRYPOINT ["/manager"]
+CMD /usr/src/manager
diff --git a/dev-support/docker-images/operator-v3/build.sh b/dev-support/docker-images/operator-v3/build.sh
new file mode 100755
index 00000000..eca60d94
--- /dev/null
+++ b/dev-support/docker-images/operator-v3/build.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+set -euxo pipefail
+
+SUBMARINE_VERSION=0.8.0-SNAPSHOT
+SUBMARINE_IMAGE_NAME="apache/submarine:operator-${SUBMARINE_VERSION}"
+
+if [ -L ${BASH_SOURCE-$0} ]; then
+ PWD=$(dirname $(readlink "${BASH_SOURCE-$0}"))
+else
+ PWD=$(dirname ${BASH_SOURCE-$0})
+fi
+export CURRENT_PATH=$(cd "${PWD}">/dev/null; pwd)
+export SUBMARINE_HOME=${CURRENT_PATH}/../../..
+
+mkdir -p "${CURRENT_PATH}/tmp"
+cp -r ${SUBMARINE_HOME}/submarine-cloud-v3/ "${CURRENT_PATH}/tmp/submarine-cloud-v3/"
+
+cd ${CURRENT_PATH}
+echo "Start building the ${SUBMARINE_IMAGE_NAME} docker image ..."
+docker build -t ${SUBMARINE_IMAGE_NAME} .
+
+# clean temp file
+rm -rf "${CURRENT_PATH}/tmp"
diff --git a/submarine-cloud-v3/Dockerfile b/submarine-cloud-v3/Dockerfile
index 7c009d21..6efbde21 100644
--- a/submarine-cloud-v3/Dockerfile
+++ b/submarine-cloud-v3/Dockerfile
@@ -16,7 +16,8 @@
#
# Build the manager binary
-FROM golang:1.17 as builder
+FROM golang:1.17.2 as builder
+MAINTAINER Apache Software Foundation <de...@submarine.apache.org>
WORKDIR /workspace
# Copy the Go Modules manifests
@@ -32,13 +33,22 @@ COPY api/ api/
COPY controllers/ controllers/
# Build
+# use CGO_ENABLED=0 to support alpine image
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
-# Use distroless as minimal base image to package the manager binary
-# Refer to https://github.com/GoogleContainerTools/distroless for more details
-FROM gcr.io/distroless/static:nonroot
-WORKDIR /
-COPY --from=builder /workspace/manager .
-USER 65532:65532
+# use alpine to support shell params
+FROM alpine
+MAINTAINER Apache Software Foundation <de...@submarine.apache.org>
-ENTRYPOINT ["/manager"]
+WORKDIR /usr/src
+COPY artifacts/ /usr/src/artifacts/
+COPY --from=builder /workspace/manager /usr/src/manager
+
+# manager is run as non-root user
+# See config/manager/manager.yaml
+RUN addgroup -g 1000 -S submarine
+RUN adduser -u 1000 -S submarine -G submarine
+USER submarine
+
+# CMD ls
+CMD /usr/src/manager
diff --git a/submarine-cloud-v3/Makefile b/submarine-cloud-v3/Makefile
index 99c91a64..7f4b6ad2 100644
--- a/submarine-cloud-v3/Makefile
+++ b/submarine-cloud-v3/Makefile
@@ -134,7 +134,7 @@ run: manifests generate fmt vet ## Run a controller from your host.
go run ./main.go
.PHONY: docker-build
-docker-build: test ## Build docker image with the manager.
+docker-build: ## Build docker image with the manager.
docker build -t ${IMG} .
.PHONY: docker-push
diff --git a/submarine-cloud-v3/artifacts/submarine-virtualservice.yaml b/submarine-cloud-v3/artifacts/submarine-virtualservice.yaml
index 961ab006..6405ddea 100644
--- a/submarine-cloud-v3/artifacts/submarine-virtualservice.yaml
+++ b/submarine-cloud-v3/artifacts/submarine-virtualservice.yaml
@@ -24,7 +24,7 @@ spec:
hosts:
- "*"
gateways:
- - submarine/submarine-gateway
+ - submarine-cloud-v3-system/submarine-gateway
http:
- match:
- uri:
diff --git a/submarine-cloud-v3/config/default/kustomization.yaml b/submarine-cloud-v3/config/default/kustomization.yaml
index 816b193c..9bc434b2 100644
--- a/submarine-cloud-v3/config/default/kustomization.yaml
+++ b/submarine-cloud-v3/config/default/kustomization.yaml
@@ -41,11 +41,32 @@ bases:
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
#- ../prometheus
+patchesJson6902:
+# [PSP] To create a PodSecurityPolicy, uncomment all the sections with [PSP]
+# ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/
+# This configuration enables the database/minio/server to set securityContext.runAsUser
+# [PSP] To create a PodSecurityPolicy, you must set the value of cluster type as "kubernetes" or "openshift"
+# To set it to "kubernetes", uncomment the following sections
+- path: patches/psp-clustertype-kubernetes-patch.yaml
+ target:
+ group: rbac.authorization.k8s.io
+ version: v1
+ kind: ClusterRole
+ name: manager-role
+# [PSP] To create a PodSecurityPolicy, you must set the value of cluster type as "kubernetes" or "openshift"
+# To set it to "openshift", uncomment the following sections
+# - path: patches/psp-clustertype-openshift-patch.yaml
+# target:
+# group: rbac.authorization.k8s.io
+# version: v1
+# kind: ClusterRole
+# name: manager-role
+
patchesStrategicMerge:
# Protect the /metrics endpoint by putting it behind auth.
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
-- manager_auth_proxy_patch.yaml
+# - manager_auth_proxy_patch.yaml
# Mount the controller config file for loading manager configurations
# through a ComponentConfig type
diff --git a/submarine-cloud-v3/Dockerfile b/submarine-cloud-v3/config/default/patches/psp-clustertype-kubernetes-patch.yaml
similarity index 50%
copy from submarine-cloud-v3/Dockerfile
copy to submarine-cloud-v3/config/default/patches/psp-clustertype-kubernetes-patch.yaml
index 7c009d21..50679094 100644
--- a/submarine-cloud-v3/Dockerfile
+++ b/submarine-cloud-v3/config/default/patches/psp-clustertype-kubernetes-patch.yaml
@@ -15,30 +15,14 @@
# limitations under the License.
#
-# Build the manager binary
-FROM golang:1.17 as builder
-
-WORKDIR /workspace
-# Copy the Go Modules manifests
-COPY go.mod go.mod
-COPY go.sum go.sum
-# cache deps before building and copying source so that we don't need to re-download as much
-# and so that source changes don't invalidate our downloaded layer
-RUN go mod download
-
-# Copy the go source
-COPY main.go main.go
-COPY api/ api/
-COPY controllers/ controllers/
-
-# Build
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
-
-# Use distroless as minimal base image to package the manager binary
-# Refer to https://github.com/GoogleContainerTools/distroless for more details
-FROM gcr.io/distroless/static:nonroot
-WORKDIR /
-COPY --from=builder /workspace/manager .
-USER 65532:65532
-
-ENTRYPOINT ["/manager"]
+- op: add
+ path: /rules/-
+ value:
+ apiGroups:
+ - policy
+ resources:
+ - podsecuritypolicies
+ verbs:
+ - use
+ resourceNames:
+ - submarine-anyuid
diff --git a/submarine-cloud-v3/Dockerfile b/submarine-cloud-v3/config/default/patches/psp-clustertype-openshift-patch.yaml
similarity index 50%
copy from submarine-cloud-v3/Dockerfile
copy to submarine-cloud-v3/config/default/patches/psp-clustertype-openshift-patch.yaml
index 7c009d21..3abd126e 100644
--- a/submarine-cloud-v3/Dockerfile
+++ b/submarine-cloud-v3/config/default/patches/psp-clustertype-openshift-patch.yaml
@@ -15,30 +15,14 @@
# limitations under the License.
#
-# Build the manager binary
-FROM golang:1.17 as builder
-
-WORKDIR /workspace
-# Copy the Go Modules manifests
-COPY go.mod go.mod
-COPY go.sum go.sum
-# cache deps before building and copying source so that we don't need to re-download as much
-# and so that source changes don't invalidate our downloaded layer
-RUN go mod download
-
-# Copy the go source
-COPY main.go main.go
-COPY api/ api/
-COPY controllers/ controllers/
-
-# Build
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
-
-# Use distroless as minimal base image to package the manager binary
-# Refer to https://github.com/GoogleContainerTools/distroless for more details
-FROM gcr.io/distroless/static:nonroot
-WORKDIR /
-COPY --from=builder /workspace/manager .
-USER 65532:65532
-
-ENTRYPOINT ["/manager"]
+- op: add
+ path: /rules/-
+ value:
+ apiGroups:
+ - security.openshift.io
+ resources:
+ - securitycontextconstraints
+ verbs:
+ - use
+ resourceNames:
+ - anyuid
diff --git a/submarine-cloud-v3/config/manager/manager.yaml b/submarine-cloud-v3/config/manager/manager.yaml
index a0e1d4a0..b91ea946 100644
--- a/submarine-cloud-v3/config/manager/manager.yaml
+++ b/submarine-cloud-v3/config/manager/manager.yaml
@@ -20,6 +20,7 @@ kind: Namespace
metadata:
labels:
control-plane: controller-manager
+ istio-injection: enabled
name: system
---
apiVersion: apps/v1
@@ -43,11 +44,13 @@ spec:
spec:
securityContext:
runAsNonRoot: true
+ runAsUser: 1000
containers:
- command:
- - /manager
+ - /usr/src/manager
args:
- - --leader-elect
+ - -clustertype=kubernetes
+ - -createpsp=true
image: controller:latest
name: manager
securityContext:
diff --git a/submarine-cloud-v3/config/rbac/role.yaml b/submarine-cloud-v3/config/rbac/role.yaml
index 7384f94b..cdb8c456 100644
--- a/submarine-cloud-v3/config/rbac/role.yaml
+++ b/submarine-cloud-v3/config/rbac/role.yaml
@@ -22,6 +22,16 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - jobs
+ - namespaces
+ - pods
+ - secrets
+ verbs:
+ - '*'
- apiGroups:
- ""
resources:
@@ -30,101 +40,54 @@ rules:
- create
- patch
- apiGroups:
- - apps
+ - apiextensions.k8s.io
resources:
- - deployments
+ - customresourcedefinitions
verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
+ - '*'
- apiGroups:
- apps
resources:
+ - deployments
- statefulsets
verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
+ - '*'
- apiGroups:
- - ""
+ - apps
resources:
- - persistentvolumeclaims
+ - replicasets
verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
+ - '*'
- apiGroups:
- ""
resources:
+ - persistentvolumeclaims
- serviceaccounts
+ - services
verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
+ - '*'
- apiGroups:
- - ""
+ - kubeflow.org
resources:
- - services
+ - notebooks
+ - pytorchjobs
+ - tfjobs
+ - xgboostjobs
verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
+ - '*'
- apiGroups:
- networking.istio.io
resources:
- virtualservices
verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
+ - '*'
- apiGroups:
- - rbac
+ - rbac.authorization.k8s.io
resources:
- rolebindings
- verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
-- apiGroups:
- - rbac
- resources:
- roles
verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
+ - '*'
- apiGroups:
- submarine.apache.org
resources:
diff --git a/submarine-cloud-v3/controllers/submarine_controller.go b/submarine-cloud-v3/controllers/submarine_controller.go
index fa334c8f..4c1d29bf 100644
--- a/submarine-cloud-v3/controllers/submarine_controller.go
+++ b/submarine-cloud-v3/controllers/submarine_controller.go
@@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "time"
"github.com/go-logr/logr"
@@ -119,23 +120,27 @@ type SubmarineReconciler struct {
// On change, run `make manifest` to update config/rbac/role.yaml
// Reference: https://github.com/apache/submarine/blob/master/submarine-cloud-v3/config/rbac/role.yaml
-// Submarine resource
+// Submarine resources
//+kubebuilder:rbac:groups=submarine.apache.org,resources=submarines,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=submarine.apache.org,resources=submarines/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=submarine.apache.org,resources=submarines/finalizers,verbs=update
// Event
//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch
+//+kubebuilder:rbac:groups="",resources=pods;secrets;configmaps;namespaces;jobs,verbs="*"
+//+kubebuilder:rbac:groups="apiextensions.k8s.io",resources=customresourcedefinitions,verbs="*"
-// Other resources
-//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=rbac,resources=roles,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=rbac,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=networking.istio.io,resources=virtualservices,verbs=get;list;watch;create;update;patch;delete
+// k8s resources
+//+kubebuilder:rbac:groups=apps,resources=deployments;statefulsets,verbs="*"
+//+kubebuilder:rbac:groups=apps,resources=replicasets,verbs="*"
+//+kubebuilder:rbac:groups=core,resources=persistentvolumeclaims;serviceaccounts;services,verbs="*"
+//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;rolebindings,verbs="*"
+
+// kubeflow resources
+//+kubebuilder:rbac:groups=kubeflow.org,resources=notebooks;pytorchjobs;tfjobs;xgboostjobs,verbs="*"
+
+// Istio resources
+//+kubebuilder:rbac:groups=networking.istio.io,resources=virtualservices,verbs="*"
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
@@ -222,7 +227,10 @@ func (r *SubmarineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}
- return ctrl.Result{}, nil
+ // Re-run Reconcile regularly
+ result := ctrl.Result{}
+ result.RequeueAfter = time.Second * 30 // default resync period
+ return result, nil
}
func (r *SubmarineReconciler) updateSubmarineStatus(ctx context.Context, submarine, submarineCopy *submarineapacheorgv1alpha1.Submarine) error {
diff --git a/submarine-cloud-v3/controllers/submarine_controller_test.go b/submarine-cloud-v3/controllers/submarine_controller_test.go
new file mode 100644
index 00000000..741e3e87
--- /dev/null
+++ b/submarine-cloud-v3/controllers/submarine_controller_test.go
@@ -0,0 +1,210 @@
+/*
+Copyright 2022.
+
+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 controllers
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "time"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/pkg/errors"
+ corev1 "k8s.io/api/core/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/util/yaml"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ submarineapacheorgv1alpha1 "github.com/apache/submarine/submarine-cloud-v3/api/v1alpha1"
+)
+
+var _ = Describe("Submarine controller", func() {
+
+ // Define utility constants and variables
+ const (
+ submarineNamespace = "submarine-test-submit-custom-resource" // The namespace where the Submarine CR is created
+
+ createNsTimeout = time.Second * 10
+ createNsInterval = time.Second * 2
+ createSubmarineTimeout = time.Second * 1200
+ createSubmarineInterval = time.Second * 10
+ deleteSubmarineTimeout = time.Second * 600
+ deleteSubmarineInterval = time.Second * 10
+ deleteNsTimeout = time.Second * 120
+ deleteNsInterval = time.Second * 2
+ )
+ var (
+ // The name of Submarine is specified in the YAML file.
+ // Storing name to call k8sClient.Get with NamespacedName
+ submarineName string
+
+ ctx = context.Background()
+ )
+
+ Context("Create a test namespace", func() {
+ It("Should create a test namespace", func() {
+ msg := fmt.Sprintf("Creating the test namespace %s", submarineNamespace)
+ By(msg)
+
+ ns := &corev1.Namespace{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: submarineNamespace,
+ Labels: map[string]string{
+ "istio-injection": "enabled",
+ },
+ },
+ }
+ Expect(k8sClient.Create(ctx, ns)).Should(Succeed())
+
+ // We'll need to retry getting this newly created namespace, given that creation may not immediately happen.
+ createdNs := &corev1.Namespace{} // stub
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, types.NamespacedName{Name: submarineNamespace, Namespace: "default"}, createdNs)
+ if err != nil {
+ return false
+ }
+ return true
+ }, createNsTimeout, createNsInterval).Should(BeTrue())
+
+ // The namespace should have Istio label
+ Expect(createdNs.Labels["istio-injection"]).To(Equal("enabled"))
+ })
+ })
+
+ Context("Create Submarine", func() {
+ It("Should create a Submarine and it should become RUNNING", func() {
+ By("Creating a new Submarine")
+ submarine, err := MakeSubmarineFromYaml("../config/samples/_v1alpha1_submarine.yaml")
+ Expect(err).To(BeNil())
+
+ // The name of Submarine is specified in the YAML file.
+ // Storing name to call k8sClient.Get with NamespacedName
+ submarineName = submarine.Name
+
+ // Create Submarine in our namespace
+ submarine.Namespace = submarineNamespace
+ Expect(k8sClient.Create(ctx, submarine)).Should(Succeed())
+
+ // We'll need to retry getting this newly created Submarine, given that creation may not immediately happen.
+ createdSubmarine := &submarineapacheorgv1alpha1.Submarine{} // stub
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, types.NamespacedName{Name: submarineName, Namespace: submarineNamespace}, createdSubmarine)
+ if err != nil {
+ return false
+ }
+ return true
+ }, createNsTimeout, createNsInterval).Should(BeTrue())
+
+ // Wait for Submarine to be in RUNNING state
+ msg := fmt.Sprintf("Waiting until Submarine %s/%s become RUNNING", submarineName, submarineNamespace)
+ By(msg)
+ Eventually(func() bool {
+ err = k8sClient.Get(ctx, types.NamespacedName{Name: submarineName, Namespace: submarineNamespace}, createdSubmarine)
+ Expect(err).To(BeNil())
+
+ state := createdSubmarine.Status.SubmarineState.State
+ Expect(state).ToNot(Equal(submarineapacheorgv1alpha1.FailedState))
+ if createdSubmarine.Status.SubmarineState.State == submarineapacheorgv1alpha1.RunningState {
+ return true
+ }
+ return false
+ }, createSubmarineTimeout, createSubmarineInterval).Should(BeTrue())
+ })
+ })
+
+ Context("Delete Submarine", func() {
+ It("Should delete the Submarine", func() {
+ Expect(submarineName).ToNot(BeNil())
+
+ msg := fmt.Sprintf("Deleting Submarine %s/%s", submarineName, submarineNamespace)
+ By(msg)
+
+ createdSubmarine := &submarineapacheorgv1alpha1.Submarine{} // stub
+ err := k8sClient.Get(ctx, types.NamespacedName{Name: submarineName, Namespace: submarineNamespace}, createdSubmarine)
+ Expect(err).To(BeNil())
+
+ foreground := metav1.DeletePropagationForeground
+ err = k8sClient.Delete(ctx, createdSubmarine, &client.DeleteOptions{
+ PropagationPolicy: &foreground,
+ })
+ Expect(err).To(BeNil())
+
+ // Wait for Submarine to be deleted entirely
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, types.NamespacedName{Name: submarineName, Namespace: submarineNamespace}, createdSubmarine)
+ if apierrors.IsNotFound(err) {
+ return true
+ }
+ Expect(err).To(BeNil())
+ return false
+ }, deleteSubmarineTimeout, deleteSubmarineInterval).Should(BeTrue())
+ })
+ })
+
+ Context("Delete the test namespace", func() {
+ It("Should delete the test namespace", func() {
+ msg := fmt.Sprintf("Deleting the test namespace %s", submarineNamespace)
+ By(msg)
+
+ createdNs := &corev1.Namespace{} // stub
+ Expect(k8sClient.Get(ctx, types.NamespacedName{Name: submarineNamespace, Namespace: "default"}, createdNs)).Should(Succeed())
+ Expect(k8sClient.Delete(ctx, createdNs)).Should(Succeed())
+
+ // Wait for submarine to be deleted entirely
+
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, types.NamespacedName{Name: submarineNamespace, Namespace: "default"}, createdNs)
+ if apierrors.IsNotFound(err) {
+ return true
+ }
+ Expect(err).To(BeNil())
+ return false
+ }, deleteNsTimeout, deleteNsInterval).Should(BeTrue())
+ })
+ })
+})
+
+func MakeSubmarineFromYaml(pathToYaml string) (*submarineapacheorgv1alpha1.Submarine, error) {
+ manifest, err := PathToOSFile(pathToYaml)
+ if err != nil {
+ return nil, err
+ }
+ tmp := submarineapacheorgv1alpha1.Submarine{}
+ if err := yaml.NewYAMLOrJSONDecoder(manifest, 100).Decode(&tmp); err != nil {
+ return nil, errors.Wrap(err, fmt.Sprintf("failed to decode file %s", pathToYaml))
+ }
+ return &tmp, err
+}
+
+// PathToOSFile gets the absolute path from relative path.
+func PathToOSFile(relativePath string) (*os.File, error) {
+ path, err := filepath.Abs(relativePath)
+ if err != nil {
+ return nil, errors.Wrap(err, fmt.Sprintf("failed generate absolute file path of %s", relativePath))
+ }
+
+ manifest, err := os.Open(path)
+ if err != nil {
+ return nil, errors.Wrap(err, fmt.Sprintf("failed to open file %s", path))
+ }
+
+ return manifest, nil
+}
diff --git a/submarine-cloud-v3/controllers/suite_test.go b/submarine-cloud-v3/controllers/suite_test.go
index b73422a3..39b4f485 100644
--- a/submarine-cloud-v3/controllers/suite_test.go
+++ b/submarine-cloud-v3/controllers/suite_test.go
@@ -17,12 +17,15 @@ limitations under the License.
package controllers
import (
+ "context"
"path/filepath"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
- "k8s.io/client-go/kubernetes/scheme"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
@@ -30,6 +33,8 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
+ istioscheme "istio.io/client-go/pkg/clientset/versioned/scheme"
+
submarineapacheorgv1alpha1 "github.com/apache/submarine/submarine-cloud-v3/api/v1alpha1"
//+kubebuilder:scaffold:imports
)
@@ -40,6 +45,9 @@ import (
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
+var scheme *runtime.Scheme
+var ctx context.Context
+var cancel context.CancelFunc
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
@@ -50,30 +58,44 @@ func TestAPIs(t *testing.T) {
}
var _ = BeforeSuite(func() {
+ // Setup ctx and cancel, which are used in BeforeSuite and AfterSuite first,
+ // in case BeforeSuite fails
+ ctx, cancel = context.WithCancel(context.TODO())
+
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
By("bootstrapping test environment")
+ useExistingCluster := true
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
+ UseExistingCluster: &useExistingCluster,
}
cfg, err := testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
- err = submarineapacheorgv1alpha1.AddToScheme(scheme.Scheme)
+ By("adding required schemes")
+ scheme = runtime.NewScheme()
+ err = clientgoscheme.AddToScheme(scheme)
+ Expect(err).NotTo(HaveOccurred())
+ err = istioscheme.AddToScheme(scheme)
+ Expect(err).NotTo(HaveOccurred())
+ err = submarineapacheorgv1alpha1.AddToScheme(scheme)
Expect(err).NotTo(HaveOccurred())
//+kubebuilder:scaffold:scheme
- k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ By("creating k8s client")
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
-
-}, 60)
+}, 60) // timeout, seconds
var _ = AfterSuite(func() {
+ cancel()
+
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
diff --git a/submarine-cloud-v3/docs/developer-guide.md b/submarine-cloud-v3/docs/developer-guide.md
index 8250f315..75badc02 100644
--- a/submarine-cloud-v3/docs/developer-guide.md
+++ b/submarine-cloud-v3/docs/developer-guide.md
@@ -22,16 +22,16 @@ Kubernetes version: `1.21.0`
## Prerequisites
-First finish the prerequisites specified in the [QuickStart](https://submarine.apache.org/docs/next/gettingStarted/quickstart) section on the submarine website. Prepare a minikube cluster with Istio installed. Prepare namespaces `submarine` and `submarine-user-test` and label them `istio-injection=enabled`.
+First finish the prerequisites specified in the [QuickStart](https://submarine.apache.org/docs/next/gettingStarted/quickstart) section on the submarine website. Prepare a minikube cluster with Istio installed. Prepare namespaces `submarine-user-test` and `submarine-cloud-v3-system` and label them `istio-injection=enabled`.
Verify with kubectl:
```bash
$ kubectl get namespace --show-labels
-NAME STATUS AGE LABELS
-istio-system Active 7d8h kubernetes.io/metadata.name=istio-system
-submarine Active 7d8h istio-injection=enabled,kubernetes.io/metadata.name=submarine
-submarine-user-test Active 27h istio-injection=enabled,kubernetes.io/metadata.name=submarine-user-test
+NAME STATUS AGE LABELS
+istio-system Active 7d8h kubernetes.io/metadata.name=istio-system
+submarine-user-test Active 27h istio-injection=enabled,kubernetes.io/metadata.name=submarine-user-test
+submarine-cloud-v3-system Active 27h istio-injection=enabled,kubernetes.io/metadata.name=submarine-submarine-cloud-v3-system
$ kubectl get pod -n istio-system
NAME READY STATUS RESTARTS AGE
@@ -39,16 +39,20 @@ istio-ingressgateway-77968dbd74-wq4vb 1/1 Running 1 7d4h
istiod-699b647f8b-nx9rt 1/1 Running 2 7d4h
```
-Next, install submarine dependencies with helm. `--set dev=true` option will not install the operator deployment to the cluster.
+## Run operator out-of-cluster
+
+Before running submarine operator, install submarine dependencies with helm. `--set dev=true` option will not install the operator deployment to the cluster.
```bash
-helm install --set dev=true submarine ../helm-charts/submarine/ -n submarine
+helm install --set dev=true submarine ../helm-charts/submarine/ -n submarine-cloud-v3-system
```
-## Run operator out-of-cluster
+Now we run the submarine operator.
```bash
# Step1: Apply the submarine CRD.
+# Note that the submarine CRD /helm-charts/submarine/crds/crd.yaml is for submarine-cloud-v2.
+# This step will overwrite it with a CRD generated with controller-gen.
make install
# Step2: Run the operator in a terminal.
@@ -73,31 +77,53 @@ make uninstall
```
## Run operator in-cluster
+
+Note that running `make deploy` `make undeploy` creates and deletes `submarine-cloud-v3-system` respectively. Therefore we will run `helm uninstall` before `make undeploy`.
+
```bash
# Step1: Build the docker image.
eval $(minikube docker-env)
make docker-build
eval $(minikube docker-env -u)
-# Step2: Deploy the operator.
-# A new namespace is created with name submarine-cloud-v3-system, and will be used for the deployment.
+# Step2: Install submarine dependencies with Helm
+# If the namespace submarine-cloud-v3-system doesn't exist yet,
+# running Step3 will create it.
+# However, note that if podSecurityPolicy is enabled,
+# the submarine operator pod will not be permitted until running this
+helm install --set dev=true submarine ../helm-charts/submarine/ -n submarine-cloud-v3-system
+
+# Step3: Deploy the operator.
+# 1) Note that the submarine CRD /helm-charts/submarine/crds/crd.yaml is for submarine-cloud-v2.
+# This step will overwrite it with a CRD generated with controller-gen.
+# 2) A new namespace is created with name submarine-cloud-v3-system, and will be used for the deployment.
+# If such a namespace already exists, this step will overwrite its spec.
+# 3) Other resources are created in this namespaces
make deploy
-# Step3: Verify the operator is up and running.
+# Step4: Verify the operator is up and running.
kubectl get deployment -n submarine-cloud-v3-system
-# Step4: Deploy a submarine.
+# Step5: Deploy a submarine.
kubectl apply -n submarine-user-test -f config/samples/_v1alpha1_submarine.yaml
# You can now view the submarine workbench
-# Step5: Cleanup submarine.
+# Step6: Cleanup submarine.
kubectl delete -n submarine-user-test submarine example-submarine
-# Step6: Cleanup operator.
+# Step7: Cleanup submarine dependencies.
+helm uninstall submarine -n submarine-cloud-v3-system
+
+# Step8: Cleanup operator.
+# Note that this step deletes the namespace submarine-cloud-v3-system
make undeploy
```
+### Installing Helm in a different namespace
+
+By default, the Istio virtual service created by the operator binds to the Istio gateway `submarine-cloud-v3-system/submarine-gateway`, which should be installed in the `submarine-cloud-v3-system` namespace via Helm. If `helm install` is run with `-n <your_namespace>`, edit the spec `spec.gateways` of the virtual service to be `<your_namespace>/submarine-gateway` manually once it's created by the operator.
+
### Rebuild Operator Image
When running operator in-cluster, we need to rebuild the operator image for changes to take effect.
@@ -133,3 +159,7 @@ Steps to add new resource created and controlled by the operator:
4. If needed, import new scheme in `main.go`.
5. If there are new resource types, add RBAC marker comments in `contorllers/submarine_controller.go`.
6. Run `make manifests` to update cluster role rules in `config/rbac/role.yaml`.
+
+## Run Operator End-to-end Tests
+
+Have the submarine operator running in-cluster or out-of-cluster before running `make test`. For verbose mode, instead run `go test ./controllers/ -v -ginkgo.v`, which outputs anything written to `GinkgoWriter` to stdout.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org