提交 d245b8e9 编写于 作者: L liqingping

feat: remove webhook

上级 88e32380
[![Build](https://github.com/opendilab/DI-orchestrator/actions/workflows/build.yaml/badge.svg?branch=main)](https://github.com/opendilab/DI-orchestrator/actions/workflows/build.yaml) [![Releases](https://github.com/opendilab/DI-orchestrator/actions/workflows/release.yaml/badge.svg)](https://github.com/opendilab/DI-orchestrator/actions/workflows/release.yaml)
# DI Orchestrator
DI Orchestrator is designed to manage DI (Decision Intelligence) jobs using Kubernetes Custom Resource and Operator.
DI Orchestrator is designed to manage DI ([Decision Intelligence](https://github.com/opendilab/DI-engine/)) jobs using Kubernetes Custom Resource and Operator.
### Prerequisites
- A well-prepared kubernetes cluster. Follow the [instructions](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/) to create a kubernetes cluster, or create a local kubernetes node referring to [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) or [minikube](https://minikube.sigs.k8s.io/docs/start/)
- Cert-manager. Installation on kubernetes please refer to [cert-manager docs](https://cert-manager.io/docs/installation/kubernetes/). Or you can install it by the following command.
```bash
kubectl create -f ./config/certmanager/cert-manager.yaml
```
### Install DI Orchestrator
DI Orchestrator consists of three components: `di-operator`, `di-webhook` and `di-server`. Install them with the following command.
DI Orchestrator consists of two components: `di-operator` and `di-server`. Install them with the following command.
```bash
kubectl create -f ./config/di-manager.yaml
```
`di-operator`, `di-webhook` and `di-server` will be installed in `di-system` namespace.
`di-operator` and `di-server` will be installed in `di-system` namespace.
```bash
$ kubectl get pod -n di-system
NAME READY STATUS RESTARTS AGE
di-operator-57cc65d5c9-5vnvn 1/1 Running 0 59s
di-server-7b86ff8df4-jfgmp 1/1 Running 0 59s
di-webhook-45jgi23fhc-9yght 1/1 Running 0 59s
```
### Install AggregatorConfig
Since all DIJobs share the same configuration of aggregator, we define aggregator template in AggregatorConfig.
Install AggregatorConfig with the following command:
```bash
kubectl create -f config/samples/agconfig.yaml -n di-system
```
### Submit DIJob
```bash
# submit DIJob
$ kubectl create -f config/samples/dijob-cartpole.yaml
......@@ -44,9 +40,11 @@ $ kubectl logs cartpole-dqn-coordinator
```
## User Guide
Refers to [user-guide](./docs/architecture.md). For Chinese version, please refer to [中文手册](./docs/architecture-cn.md)
## Contributing
Refers to [developer-guide](./docs/developer-guide.md).
Refers to [developer-guide](./docs/developer-guide.md).
Contact us throw <opendilab.contact@gmail.com>
......@@ -23,7 +23,6 @@ import (
"opendilab.org/di-orchestrator/cmd/operator"
"opendilab.org/di-orchestrator/cmd/server"
"opendilab.org/di-orchestrator/cmd/webhook"
)
// rootCmd represents the base command when called without any subcommands
......@@ -50,7 +49,6 @@ func Execute() {
func init() {
rootCmd.AddCommand(server.NewCmdServer())
rootCmd.AddCommand(operator.NewCmdOperator())
rootCmd.AddCommand(webhook.NewCmdWebhook())
// add all the flags in go flagset into pflagset
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
......
/*
Copyright 2021 The OpenDILab 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 webhook
import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
cmdcommon "opendilab.org/di-orchestrator/cmd/common"
div1alpha2 "opendilab.org/di-orchestrator/pkg/api/v1alpha2"
)
type CreateOptions struct {
MetricAddress string
ProbeAddress string
EnableLeaderElection bool
Port int
}
func NewCreateOptions() *CreateOptions {
return &CreateOptions{
MetricAddress: ":8443",
ProbeAddress: ":8080",
EnableLeaderElection: false,
Port: 9443,
}
}
func (o *CreateOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.MetricAddress, "metric-addr", o.MetricAddress, "The address the metric endpoint binds to.")
cmd.Flags().StringVar(&o.ProbeAddress, "probe-addr", o.ProbeAddress, "The address the probe endpoint binds to.")
cmd.Flags().BoolVar(&o.EnableLeaderElection, "leader-elect", o.EnableLeaderElection,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
cmd.Flags().IntVarP(&o.Port, "port", "p", o.Port, "The port the webhook endpoint binds to.")
}
func NewCmdWebhook() *cobra.Command {
o := NewCreateOptions()
var webhookCmd = &cobra.Command{
Use: "webhook",
Short: "Command to run di-webhook ",
Long: `Run di-webhook with specified configuration.
Examples:
# Start di-webhook with port specified.
di-orchestrator webhook --port 9443
`,
Run: func(cmd *cobra.Command, args []string) {
cobra.CheckErr(runCommand(cmd, o))
},
}
o.AddFlags(webhookCmd)
return webhookCmd
}
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(div1alpha2.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme
}
func runCommand(cmd *cobra.Command, options *CreateOptions) error {
ctrl.SetLogger(cmdcommon.Logger)
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: options.MetricAddress,
Port: options.Port,
HealthProbeBindAddress: options.ProbeAddress,
LeaderElection: options.EnableLeaderElection,
LeaderElectionID: "67841a5d.opendilab.org",
})
if err != nil {
setupLog.Error(err, "unable to start manager")
return err
}
if err = (&div1alpha2.DIJob{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "DIJob")
return err
}
//+kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
return err
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
return err
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
return err
}
return nil
}
......@@ -18,9 +18,9 @@ bases:
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
- ../webhook
# - ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
- ../certmanager
# - ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
#- ../prometheus
......@@ -36,38 +36,38 @@ patchesStrategicMerge:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
- manager_webhook_patch.yaml
# - manager_webhook_patch.yaml
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
# 'CERTMANAGER' needs to be enabled to use ca injection
- webhookcainjection_patch.yaml
# - webhookcainjection_patch.yaml
vars:
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
objref:
kind: Certificate
group: cert-manager.io
version: v1
name: di-serving-cert # this name should match the one in certificate.yaml
fieldref:
fieldpath: metadata.namespace
- name: CERTIFICATE_NAME
objref:
kind: Certificate
group: cert-manager.io
version: v1
name: di-serving-cert # this name should match the one in certificate.yaml
- name: SERVICE_NAMESPACE # namespace of the service
objref:
kind: Service
version: v1
name: di-webhook-service
fieldref:
fieldpath: metadata.namespace
- name: SERVICE_NAME
objref:
kind: Service
version: v1
name: di-webhook-service
\ No newline at end of file
# - name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: di-serving-cert # this name should match the one in certificate.yaml
# fieldref:
# fieldpath: metadata.namespace
# - name: CERTIFICATE_NAME
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: di-serving-cert # this name should match the one in certificate.yaml
# - name: SERVICE_NAMESPACE # namespace of the service
# objref:
# kind: Service
# version: v1
# name: di-webhook-service
# fieldref:
# fieldpath: metadata.namespace
# - name: SERVICE_NAME
# objref:
# kind: Service
# version: v1
# name: di-webhook-service
\ No newline at end of file
......@@ -9,7 +9,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: di-system/di-serving-cert
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
controller-gen.kubebuilder.io/version: v0.4.1
name: dijobs.diengine.opendilab.org
spec:
......@@ -18,7 +18,7 @@ spec:
webhook:
clientConfig:
service:
name: di-webhook-service
name: $(SERVICE_NAME)
namespace: di-system
path: /mutate-diengine-opendilab-org-v1alpha2-dijob
conversionReviewVersions:
......@@ -4110,18 +4110,6 @@ spec:
selector:
control-plane: di-server
---
apiVersion: v1
kind: Service
metadata:
name: di-webhook-service
namespace: di-system
spec:
ports:
- port: 443
targetPort: 9443
selector:
control-plane: di-webhook
---
apiVersion: apps/v1
kind: Deployment
metadata:
......@@ -4216,147 +4204,3 @@ spec:
securityContext:
allowPrivilegeEscalation: false
terminationGracePeriodSeconds: 10
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
control-plane: di-webhook
name: di-webhook
namespace: di-system
spec:
replicas: 1
selector:
matchLabels:
control-plane: di-webhook
template:
metadata:
labels:
control-plane: di-webhook
spec:
containers:
- args:
- --probe-addr=:8080
- --metric-addr=:8443
- --port=9443
command:
- /di-orchestrator
- webhook
image: registry.sensetime.com/cloudnative4ai/di-orchestrator:v1.0.0
imagePullPolicy: Always
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
name: webhook
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 30m
memory: 100Mi
requests:
cpu: 30m
memory: 100Mi
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: cert
secret:
defaultMode: 420
secretName: di-webhook-server-cert
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: di-serving-cert
namespace: di-system
spec:
dnsNames:
- di-webhook-service.di-system.svc
- di-webhook-service.di-system.svc.cluster.local
issuerRef:
kind: Issuer
name: di-selfsigned-issuer
secretName: di-webhook-server-cert
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: di-selfsigned-issuer
namespace: di-system
spec:
selfSigned: {}
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: di-system/di-serving-cert
name: di-mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: di-webhook-service
namespace: di-system
path: /mutate-diengine-opendilab-org-v1alpha2-dijob
failurePolicy: Fail
name: mdijob.kb.io
rules:
- apiGroups:
- diengine.opendilab.org
apiVersions:
- v1alpha2
operations:
- CREATE
- UPDATE
resources:
- dijobs
sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: di-system/di-serving-cert
name: di-validating-webhook-configuration
webhooks:
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: di-webhook-service
namespace: di-system
path: /validate-diengine-opendilab-org-v1alpha2-dijob
failurePolicy: Fail
name: vdijob.kb.io
rules:
- apiGroups:
- diengine.opendilab.org
apiVersions:
- v1alpha2
operations:
- CREATE
- UPDATE
resources:
- dijobs
sideEffects: None
resources:
- di_operator.yaml
- di_server.yaml
- di_webhook.yaml
# - di_webhook.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
......
/*
Copyright 2021 The OpenDILab 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 v1alpha2
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
// log is for logging in this package.
var dijoblog = logf.Log.WithName("dijob-resource")
func (r *DIJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
//+kubebuilder:webhook:path=/mutate-diengine-opendilab-org-v1alpha2-dijob,mutating=true,failurePolicy=fail,sideEffects=None,groups=diengine.opendilab.org,resources=dijobs,verbs=create;update,versions=v1alpha2,name=mdijob.kb.io,admissionReviewVersions={v1,v1beta1}
var _ webhook.Defaulter = &DIJob{}
// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *DIJob) Default() {
dijoblog.Info("default", "name", r.Name)
if r.Spec.CleanPodPolicy == "" {
r.Spec.CleanPodPolicy = CleanPodPolicyRunning
}
}
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
//+kubebuilder:webhook:path=/validate-diengine-opendilab-org-v1alpha2-dijob,mutating=false,failurePolicy=fail,sideEffects=None,groups=diengine.opendilab.org,resources=dijobs,verbs=create;update,versions=v1alpha2,name=vdijob.kb.io,admissionReviewVersions={v1,v1beta1}
var _ webhook.Validator = &DIJob{}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *DIJob) ValidateCreate() error {
dijoblog.Info("validate create", "name", r.Name)
// TODO(user): fill in your validation logic upon object creation.
if r.Spec.CleanPodPolicy != CleanPodPolicyAll && r.Spec.CleanPodPolicy != CleanPodPolicyNone &&
r.Spec.CleanPodPolicy != CleanPodPolicyRunning {
return fmt.Errorf("Invalid CleanPodPolicy %s, expected in [%s, %s, %s]",
r.Spec.CleanPodPolicy, CleanPodPolicyNone, CleanPodPolicyRunning, CleanPodPolicyAll)
}
return nil
}
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *DIJob) ValidateUpdate(old runtime.Object) error {
dijoblog.Info("validate update", "name", r.Name)
// TODO(user): fill in your validation logic upon object update.
if r.Spec.CleanPodPolicy != CleanPodPolicyAll && r.Spec.CleanPodPolicy != CleanPodPolicyNone &&
r.Spec.CleanPodPolicy != CleanPodPolicyRunning {
return fmt.Errorf("Invalid CleanPodPolicy %s, expected in [%s, %s, %s]",
r.Spec.CleanPodPolicy, CleanPodPolicyNone, CleanPodPolicyRunning, CleanPodPolicyAll)
}
return nil
}
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *DIJob) ValidateDelete() error {
dijoblog.Info("validate delete", "name", r.Name)
// TODO(user): fill in your validation logic upon object deletion.
return nil
}
/*
Copyright 2021 The OpenDILab 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 v1alpha2
import (
"context"
"crypto/tls"
"fmt"
"net"
"path/filepath"
"testing"
"time"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/config"
. "github.com/onsi/gomega"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
//+kubebuilder:scaffold:imports
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
var ctx context.Context
var cancel context.CancelFunc
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Webhook Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
ctx, cancel = context.WithCancel(context.TODO())
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: false,
WebhookInstallOptions: envtest.WebhookInstallOptions{
Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
LocalServingPort: 8100 + config.GinkgoConfig.ParallelNode,
},
}
var err error
cfg, err = testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
scheme := runtime.NewScheme()
err = AddToScheme(scheme)
Expect(err).NotTo(HaveOccurred())
err = admissionv1beta1.AddToScheme(scheme)
Expect(err).NotTo(HaveOccurred())
//+kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
// start webhook server using Manager
webhookInstallOptions := &testEnv.WebhookInstallOptions
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme,
Host: webhookInstallOptions.LocalServingHost,
Port: webhookInstallOptions.LocalServingPort,
CertDir: webhookInstallOptions.LocalServingCertDir,
LeaderElection: false,
MetricsBindAddress: "0",
})
Expect(err).NotTo(HaveOccurred())
err = (&DIJob{}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())
//+kubebuilder:scaffold:webhook
go func() {
err = mgr.Start(ctx)
if err != nil {
Expect(err).NotTo(HaveOccurred())
}
}()
// wait for the webhook server to get ready
dialer := &net.Dialer{Timeout: time.Second}
addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
Eventually(func() error {
conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
if err != nil {
return err
}
conn.Close()
return nil
}).Should(Succeed())
}, 60)
var _ = AfterSuite(func() {
cancel()
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})
package v1alpha2
import (
"context"
"fmt"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilrand "k8s.io/apimachinery/pkg/util/rand"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var _ = Describe("Webhook test", func() {
Context("When creating a DIJob", func() {
It("Should be validated by webhook before creating", func() {
type testCase struct {
cleanPodPolicy CleanPodPolicy
expectCleanPodPolicy CleanPodPolicy
}
testCases := []testCase{
{cleanPodPolicy: CleanPodPolicyRunning, expectCleanPodPolicy: CleanPodPolicyRunning},
{cleanPodPolicy: CleanPodPolicyAll, expectCleanPodPolicy: CleanPodPolicyAll},
{cleanPodPolicy: CleanPodPolicyNone, expectCleanPodPolicy: CleanPodPolicyNone},
{cleanPodPolicy: CleanPodPolicy(""), expectCleanPodPolicy: CleanPodPolicyRunning},
{cleanPodPolicy: CleanPodPolicy("hello"), expectCleanPodPolicy: CleanPodPolicy("will be refused by webhook")},
{cleanPodPolicy: CleanPodPolicy("sdft"), expectCleanPodPolicy: CleanPodPolicy("will be refused by webhook")},
}
for i := range testCases {
c := testCases[i]
job := NewDIJob()
name := GenerateName(job.Name)
job.SetName(name)
job.Spec.CleanPodPolicy = c.cleanPodPolicy
var err error
ctx := context.Background()
err = k8sClient.Create(ctx, job, &client.CreateOptions{})
if err != nil {
if c.cleanPodPolicy != CleanPodPolicyRunning && c.cleanPodPolicy != CleanPodPolicyNone &&
c.cleanPodPolicy != CleanPodPolicyAll {
Expect(err.Error()).To(ContainSubstring("Invalid CleanPodPolicy"))
continue
} else {
Expect(err).NotTo(HaveOccurred())
}
}
cjob := DIJob{}
jobKey := types.NamespacedName{Namespace: job.Namespace, Name: job.Name}
Eventually(func() bool {
err = k8sClient.Get(ctx, jobKey, &cjob)
if err != nil {
return false
}
return cjob.Spec.CleanPodPolicy == c.expectCleanPodPolicy
}, timeout, interval).Should(BeTrue())
}
})
It("Should be validated by webhook before updating", func() {
type testCase struct {
cleanPodPolicy CleanPodPolicy
expectCleanPodPolicy CleanPodPolicy
}
testCases := []testCase{
{cleanPodPolicy: CleanPodPolicyRunning, expectCleanPodPolicy: CleanPodPolicyRunning},
{cleanPodPolicy: CleanPodPolicyAll, expectCleanPodPolicy: CleanPodPolicyAll},
{cleanPodPolicy: CleanPodPolicyNone, expectCleanPodPolicy: CleanPodPolicyNone},
{cleanPodPolicy: CleanPodPolicy(""), expectCleanPodPolicy: CleanPodPolicyRunning},
{cleanPodPolicy: CleanPodPolicy("hello"), expectCleanPodPolicy: CleanPodPolicy("will be refused by webhook")},
{cleanPodPolicy: CleanPodPolicy("sdft"), expectCleanPodPolicy: CleanPodPolicy("will be refused by webhook")},
}
for i := range testCases {
c := testCases[i]
job := NewDIJob()
name := GenerateName(job.Name)
job.SetName(name)
var err error
ctx := context.Background()
err = k8sClient.Create(ctx, job, &client.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
job.Spec.CleanPodPolicy = c.cleanPodPolicy
err = k8sClient.Update(ctx, job, &client.UpdateOptions{})
if err != nil {
if c.cleanPodPolicy != CleanPodPolicyRunning && c.cleanPodPolicy != CleanPodPolicyNone &&
c.cleanPodPolicy != CleanPodPolicyAll {
Expect(err.Error()).To(ContainSubstring("Invalid CleanPodPolicy"))
continue
} else {
Expect(err).NotTo(HaveOccurred())
}
}
cjob := DIJob{}
jobKey := types.NamespacedName{Namespace: job.Namespace, Name: job.Name}
Eventually(func() CleanPodPolicy {
err = k8sClient.Get(ctx, jobKey, &cjob)
if err != nil {
return CleanPodPolicy(err.Error())
}
return cjob.Spec.CleanPodPolicy
}, timeout, interval).Should(Equal(c.expectCleanPodPolicy))
}
})
})
})
const (
randomLength = 5
DIJobName = "dijob-example"
DIJobNamespace = "default"
DIJobImage = "alpine:latest"
DefaultSleepDuration = "5s"
timeout = 5 * time.Second
interval = 250 * time.Millisecond
)
func NewDIJob() *DIJob {
return &DIJob{
TypeMeta: metav1.TypeMeta{
Kind: KindDIJob,
APIVersion: GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: DIJobName,
Namespace: DIJobNamespace,
},
Spec: DIJobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "coordinator",
Image: DIJobImage,
Command: []string{"/bin/sh", "-c", "sleep", DefaultSleepDuration},
},
},
},
},
},
}
}
func GenerateName(name string) string {
return fmt.Sprintf("%s-%s", name, utilrand.String(randomLength))
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册