Unverified Commit 15462b74 authored by julienmancuso's avatar julienmancuso Committed by GitHub
Browse files

feat: make ingress configurable in operator (#717)

parent e6399307
......@@ -22,6 +22,8 @@ dynamo-operator:
enabled: true
natsAddr: "nats://${RELEASE_NAME}-nats:4222"
etcdAddr: "${RELEASE_NAME}-etcd:2379"
istioVirtualServiceEnabled: false
ingressControllerClassName: ""
namespaceRestriction:
enabled: true
targetNamespace: ${NAMESPACE}
......
......@@ -23,7 +23,7 @@ version: 25.2.0-rc3
home: https://nvidia.com
dependencies:
- name: dynamo-operator
version: 0.1.2
version: 0.1.3
repository: file://components/operator
condition: dynamo-operator.enabled
- name: dynamo-api-store
......
......@@ -27,7 +27,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.2
version: 0.1.3
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
......
......@@ -72,6 +72,12 @@ spec:
{{- if .Values.etcdAddr }}
- --etcdAddr={{ .Values.etcdAddr }}
{{- end }}
{{- if .Values.istioVirtualServiceEnabled }}
- --istio-virtual-service-enabled
{{- end }}
{{- if .Values.ingressControllerClassName }}
- --ingress-controller-class-name={{ .Values.ingressControllerClassName }}
{{- end }}
command:
- /manager
env:
......
......@@ -277,6 +277,7 @@ rules:
- patch
- update
- watch
{{- if .Values.istioVirtualServiceEnabled }}
- apiGroups:
- networking.istio.io
resources:
......@@ -289,6 +290,7 @@ rules:
- patch
- update
- watch
{{- end }}
- apiGroups:
- networking.k8s.io
resources:
......
......@@ -70,6 +70,8 @@ func main() {
var leaderElectionID string
var natsAddr string
var etcdAddr string
var istioVirtualServiceEnabled bool
var ingressControllerClassName string
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
......@@ -85,6 +87,10 @@ func main() {
"Id to use for the leader election.")
flag.StringVar(&natsAddr, "natsAddr", "", "address of the NATS server")
flag.StringVar(&etcdAddr, "etcdAddr", "", "address of the etcd server")
flag.BoolVar(&istioVirtualServiceEnabled, "istio-virtual-service-enabled", false,
"If set, the istio virtual service will be enabled for the ingress")
flag.StringVar(&ingressControllerClassName, "ingress-controller-class-name", "",
"The name of the ingress controller class to use")
opts := zap.Options{
Development: true,
}
......@@ -165,13 +171,15 @@ func main() {
os.Exit(1)
}
if err = (&controller.DynamoNimDeploymentReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("yatai-deployment"),
Config: ctrlConfig,
NatsAddr: natsAddr,
EtcdAddr: etcdAddr,
EtcdStorage: etcd.NewStorage(cli),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("yatai-deployment"),
Config: ctrlConfig,
NatsAddr: natsAddr,
EtcdAddr: etcdAddr,
EtcdStorage: etcd.NewStorage(cli),
IstioVirtualServiceEnabled: istioVirtualServiceEnabled,
IngressControllerClassName: ingressControllerClassName,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "DynamoNimDeployment")
os.Exit(1)
......
......@@ -8,6 +8,7 @@ require (
dario.cat/mergo v1.0.1
emperror.dev/errors v0.8.1
github.com/apparentlymart/go-shquot v0.0.1
github.com/bsm/gomega v1.27.10
github.com/cisco-open/k8s-objectmatcher v1.9.0
github.com/ettle/strcase v0.2.0
github.com/huandu/xstrings v1.4.0
......
......@@ -6,6 +6,8 @@ github.com/apparentlymart/go-shquot v0.0.1 h1:MGV8lwxF4zw75lN7e0MGs7o6AFYn7L6AZa
github.com/apparentlymart/go-shquot v0.0.1/go.mod h1:lw58XsE5IgUXZ9h0cxnypdx31p9mPFIVEQ9P3c7MlrU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cisco-open/k8s-objectmatcher v1.9.0 h1:/sfuO0BD09fpynZjXsqeZrh28Juc4VEwc2P6Ov/Q6fM=
......
......@@ -85,7 +85,7 @@ const (
ContainerPortNameHTTPProxy = "http-proxy"
ServicePortNameHTTPNonProxy = "http-non-proxy"
HeaderNameDebug = "X-Yatai-Debug"
kDefaultIngressSuffix = "local"
DefaultIngressSuffix = "local"
)
var ServicePortHTTPNonProxy = commonconsts.BentoServicePort + 1
......@@ -93,12 +93,14 @@ var ServicePortHTTPNonProxy = commonconsts.BentoServicePort + 1
// DynamoNimDeploymentReconciler reconciles a DynamoNimDeployment object
type DynamoNimDeploymentReconciler struct {
client.Client
Scheme *runtime.Scheme
Recorder record.EventRecorder
Config controller_common.Config
NatsAddr string
EtcdAddr string
EtcdStorage etcdStorage
Scheme *runtime.Scheme
Recorder record.EventRecorder
Config controller_common.Config
NatsAddr string
EtcdAddr string
EtcdStorage etcdStorage
IngressControllerClassName string
IstioVirtualServiceEnabled bool
}
// +kubebuilder:rbac:groups=nvidia.com,resources=dynamonimdeployments,verbs=get;list;watch;create;update;patch;delete
......@@ -380,10 +382,10 @@ func (r *DynamoNimDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.
}
// create or update api-server ingresses
modified_, _, err = createOrUpdateResource(ctx, r, generateResourceOption{
modified_, err = r.createOrUpdateOrDeleteIngress(ctx, generateResourceOption{
dynamoNimDeployment: dynamoNimDeployment,
dynamoNim: dynamoNimCR,
}, r.generateVirtualService)
})
if err != nil {
return
}
......@@ -635,6 +637,13 @@ func createOrUpdateResource[T client.Object](ctx context.Context, r *DynamoNimDe
return
}
err = ctrl.SetControllerReference(opt.dynamoNimDeployment, resource, r.Scheme)
if err != nil {
logs.Error(err, "Failed to set controller reference.")
r.Recorder.Eventf(opt.dynamoNimDeployment, corev1.EventTypeWarning, "SetControllerReference", "Failed to set controller reference for %s %s: %s", resourceType, resourceNamespace, err)
return
}
r.Recorder.Eventf(opt.dynamoNimDeployment, corev1.EventTypeNormal, fmt.Sprintf("Create%s", resourceType), "Creating a new %s %s", resourceType, resourceNamespace)
err = r.Create(ctx, resource)
if err != nil {
......@@ -785,18 +794,69 @@ func (r *DynamoNimDeploymentReconciler) createOrUpdateOrDeleteServices(ctx conte
return
}
func (r *DynamoNimDeploymentReconciler) generateVirtualService(ctx context.Context, opt generateResourceOption) (*networkingv1beta1.VirtualService, bool, error) {
func (r *DynamoNimDeploymentReconciler) createOrUpdateOrDeleteIngress(ctx context.Context, opt generateResourceOption) (modified bool, err error) {
modified, _, err = createOrUpdateResource(ctx, r, opt, r.generateIngress)
if err != nil {
return
}
modified_, _, err := createOrUpdateResource(ctx, r, opt, r.generateVirtualService)
if err != nil {
return
}
modified = modified || modified_
return
}
func (r *DynamoNimDeploymentReconciler) generateIngress(ctx context.Context, opt generateResourceOption) (*networkingv1.Ingress, bool, error) {
log := log.FromContext(ctx)
log.Info("Starting generateVirtualService")
log.Info("Starting generateIngress")
ingress := &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: opt.dynamoNimDeployment.Name,
Namespace: opt.dynamoNimDeployment.Namespace,
},
}
vsName := opt.dynamoNimDeployment.Name
if opt.dynamoNimDeployment.Spec.Ingress.HostPrefix != nil {
vsName = *opt.dynamoNimDeployment.Spec.Ingress.HostPrefix + vsName
if !opt.dynamoNimDeployment.Spec.Ingress.Enabled || r.IngressControllerClassName == "" {
log.Info("Ingress is not enabled")
return ingress, true, nil
}
ingressSuffix, found := os.LookupEnv("DYNAMO_INGRESS_SUFFIX")
if !found || ingressSuffix == "" {
ingressSuffix = kDefaultIngressSuffix
ingress.Spec = networkingv1.IngressSpec{
IngressClassName: &r.IngressControllerClassName,
Rules: []networkingv1.IngressRule{
{
Host: getIngressHost(opt.dynamoNimDeployment),
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
Path: "/",
PathType: &[]networkingv1.PathType{networkingv1.PathTypePrefix}[0],
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: opt.dynamoNimDeployment.Name,
Port: networkingv1.ServiceBackendPort{
Number: 3000,
},
},
},
},
},
},
},
},
},
}
return ingress, false, nil
}
func (r *DynamoNimDeploymentReconciler) generateVirtualService(ctx context.Context, opt generateResourceOption) (*networkingv1beta1.VirtualService, bool, error) {
log := log.FromContext(ctx)
log.Info("Starting generateVirtualService")
vs := &networkingv1beta1.VirtualService{
ObjectMeta: metav1.ObjectMeta{
Name: opt.dynamoNimDeployment.Name,
......@@ -804,7 +864,7 @@ func (r *DynamoNimDeploymentReconciler) generateVirtualService(ctx context.Conte
},
}
vsEnabled := opt.dynamoNimDeployment.Spec.Ingress.Enabled && opt.dynamoNimDeployment.Spec.Ingress.UseVirtualService != nil && *opt.dynamoNimDeployment.Spec.Ingress.UseVirtualService
vsEnabled := opt.dynamoNimDeployment.Spec.Ingress.Enabled && r.IstioVirtualServiceEnabled
if !vsEnabled {
log.Info("VirtualService is not enabled")
return vs, true, nil
......@@ -812,7 +872,7 @@ func (r *DynamoNimDeploymentReconciler) generateVirtualService(ctx context.Conte
vs.Spec = istioNetworking.VirtualService{
Hosts: []string{
fmt.Sprintf("%s.%s", vsName, ingressSuffix),
getIngressHost(opt.dynamoNimDeployment),
},
Gateways: []string{"istio-system/ingress-alb"},
Http: []*istioNetworking.HTTPRoute{
......@@ -986,11 +1046,6 @@ func (r *DynamoNimDeploymentReconciler) generateDeployment(ctx context.Context,
Strategy: strategy,
}
err = ctrl.SetControllerReference(opt.dynamoNimDeployment, kubeDeployment, r.Scheme)
if err != nil {
err = errors.Wrapf(err, "set deployment %s controller reference", kubeDeployment.Name)
}
return
}
......@@ -1003,6 +1058,18 @@ type generateResourceOption struct {
isGenericService bool
}
func getIngressHost(dynamoNimDeployment *v1alpha1.DynamoNimDeployment) string {
vsName := dynamoNimDeployment.Name
if dynamoNimDeployment.Spec.Ingress.HostPrefix != nil {
vsName = *dynamoNimDeployment.Spec.Ingress.HostPrefix + vsName
}
ingressSuffix, found := os.LookupEnv("DYNAMO_INGRESS_SUFFIX")
if !found || ingressSuffix == "" {
ingressSuffix = DefaultIngressSuffix
}
return fmt.Sprintf("%s.%s", vsName, ingressSuffix)
}
func (r *DynamoNimDeploymentReconciler) generateHPA(ctx context.Context, opt generateResourceOption) (*autoscalingv2.HorizontalPodAutoscaler, bool, error) {
labels := r.getKubeLabels(opt.dynamoNimDeployment, opt.dynamoNim)
......@@ -1057,12 +1124,7 @@ func (r *DynamoNimDeploymentReconciler) generateHPA(ctx context.Context, opt gen
}
}
err := ctrl.SetControllerReference(opt.dynamoNimDeployment, kubeHpa, r.Scheme)
if err != nil {
return nil, false, errors.Wrapf(err, "set hpa %s controller reference", kubeName)
}
return kubeHpa, false, err
return kubeHpa, false, nil
}
func getDynamoNimRepositoryNameAndDynamoNimVersion(dynamoNim *v1alpha1.DynamoNim) (repositoryName string, version string) {
......@@ -2001,12 +2063,6 @@ func (r *DynamoNimDeploymentReconciler) generateService(ctx context.Context, opt
kubeService.ObjectMeta.Labels = labels
kubeService.Spec = spec
err = ctrl.SetControllerReference(opt.dynamoNimDeployment, kubeService, r.Scheme)
if err != nil {
err = errors.Wrapf(err, "set controller reference for service %s", kubeService.Name)
return
}
return
}
......@@ -2040,7 +2096,6 @@ func (r *DynamoNimDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error
GenericFunc: func(ge event.GenericEvent) bool { return true },
})).
Owns(&corev1.Service{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&networkingv1beta1.VirtualService{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&networkingv1.Ingress{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&corev1.PersistentVolumeClaim{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Watches(&v1alpha1.DynamoNimRequest{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, dynamoNimRequest client.Object) []reconcile.Request {
......@@ -2106,6 +2161,9 @@ func (r *DynamoNimDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error
return reqs
}))
if r.IstioVirtualServiceEnabled {
m.Owns(&networkingv1beta1.VirtualService{}, builder.WithPredicates(predicate.GenerationChangedPredicate{}))
}
m.Owns(&autoscalingv2.HorizontalPodAutoscaler{})
return m.Complete(r)
}
......@@ -25,8 +25,12 @@ import (
"testing"
"github.com/ai-dynamo/dynamo/deploy/dynamo/operator/api/v1alpha1"
"github.com/bsm/gomega"
istioNetworking "istio.io/api/networking/v1beta1"
networkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
......@@ -229,3 +233,264 @@ func TestDynamoNimDeploymentReconciler_FinalizeResource(t *testing.T) {
})
}
}
func TestDynamoNimDeploymentReconciler_generateIngress(t *testing.T) {
type fields struct {
IngressControllerClassName string
IstioVirtualServiceEnabled bool
}
type args struct {
ctx context.Context
opt generateResourceOption
}
tests := []struct {
name string
fields fields
args args
want *networkingv1.Ingress
want1 bool
wantErr bool
}{
{
name: "generate ingress",
fields: fields{
IngressControllerClassName: "nginx",
IstioVirtualServiceEnabled: false,
},
args: args{
ctx: context.Background(),
opt: generateResourceOption{
dynamoNimDeployment: &v1alpha1.DynamoNimDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
Namespace: "default",
},
Spec: v1alpha1.DynamoNimDeploymentSpec{
DynamoNimDeploymentSharedSpec: v1alpha1.DynamoNimDeploymentSharedSpec{
ServiceName: "service1",
DynamoNamespace: &[]string{"default"}[0],
Ingress: v1alpha1.IngressSpec{
Enabled: true,
},
},
},
},
},
},
want: &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
Namespace: "default",
},
Spec: networkingv1.IngressSpec{
IngressClassName: &[]string{"nginx"}[0],
Rules: []networkingv1.IngressRule{
{
Host: "service1.local",
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
Path: "/",
PathType: &[]networkingv1.PathType{networkingv1.PathTypePrefix}[0],
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: "service1",
Port: networkingv1.ServiceBackendPort{Number: 3000},
},
},
},
},
},
},
},
},
},
},
want1: false,
wantErr: false,
},
{
name: "generate ingress, disabled",
fields: fields{
IngressControllerClassName: "nginx",
IstioVirtualServiceEnabled: false,
},
args: args{
ctx: context.Background(),
opt: generateResourceOption{
dynamoNimDeployment: &v1alpha1.DynamoNimDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
Namespace: "default",
},
Spec: v1alpha1.DynamoNimDeploymentSpec{
DynamoNimDeploymentSharedSpec: v1alpha1.DynamoNimDeploymentSharedSpec{
ServiceName: "service1",
DynamoNamespace: &[]string{"default"}[0],
Ingress: v1alpha1.IngressSpec{
Enabled: false,
},
},
},
},
},
},
want: &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
Namespace: "default",
},
},
want1: true,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := gomega.NewGomegaWithT(t)
r := &DynamoNimDeploymentReconciler{
IngressControllerClassName: tt.fields.IngressControllerClassName,
IstioVirtualServiceEnabled: tt.fields.IstioVirtualServiceEnabled,
}
got, got1, err := r.generateIngress(tt.args.ctx, tt.args.opt)
if (err != nil) != tt.wantErr {
t.Errorf("DynamoNimDeploymentReconciler.generateIngress() error = %v, wantErr %v", err, tt.wantErr)
return
}
g.Expect(got).To(gomega.Equal(tt.want))
g.Expect(got1).To(gomega.Equal(tt.want1))
})
}
}
func TestDynamoNimDeploymentReconciler_generateVirtualService(t *testing.T) {
type fields struct {
IngressControllerClassName string
IstioVirtualServiceEnabled bool
}
type args struct {
ctx context.Context
opt generateResourceOption
}
tests := []struct {
name string
fields fields
args args
want *networkingv1beta1.VirtualService
want1 bool
wantErr bool
}{
{
name: "generate virtual service, disabled in operator config",
fields: fields{
IngressControllerClassName: "",
IstioVirtualServiceEnabled: false,
},
args: args{
ctx: context.Background(),
opt: generateResourceOption{
dynamoNimDeployment: &v1alpha1.DynamoNimDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
Namespace: "default",
},
Spec: v1alpha1.DynamoNimDeploymentSpec{
DynamoNimDeploymentSharedSpec: v1alpha1.DynamoNimDeploymentSharedSpec{
ServiceName: "service1",
DynamoNamespace: &[]string{"default"}[0],
Ingress: v1alpha1.IngressSpec{
Enabled: true,
},
},
},
},
},
},
want: &networkingv1beta1.VirtualService{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
Namespace: "default",
},
},
want1: true,
wantErr: false,
},
{
name: "generate virtual service, enabled in operator config",
fields: fields{
IngressControllerClassName: "",
IstioVirtualServiceEnabled: true,
},
args: args{
ctx: context.Background(),
opt: generateResourceOption{
dynamoNimDeployment: &v1alpha1.DynamoNimDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
Namespace: "default",
},
Spec: v1alpha1.DynamoNimDeploymentSpec{
DynamoNimDeploymentSharedSpec: v1alpha1.DynamoNimDeploymentSharedSpec{
ServiceName: "service1",
DynamoNamespace: &[]string{"default"}[0],
Ingress: v1alpha1.IngressSpec{
Enabled: true,
},
},
},
},
},
},
want: &networkingv1beta1.VirtualService{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
Namespace: "default",
},
Spec: istioNetworking.VirtualService{
Hosts: []string{"service1.local"},
Gateways: []string{"istio-system/ingress-alb"},
Http: []*istioNetworking.HTTPRoute{
{
Match: []*istioNetworking.HTTPMatchRequest{
{
Uri: &istioNetworking.StringMatch{
MatchType: &istioNetworking.StringMatch_Prefix{Prefix: "/"},
},
},
},
Route: []*istioNetworking.HTTPRouteDestination{
{
Destination: &istioNetworking.Destination{
Host: "service1",
Port: &istioNetworking.PortSelector{
Number: 3000,
},
},
},
},
},
},
},
},
want1: false,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := gomega.NewGomegaWithT(t)
r := &DynamoNimDeploymentReconciler{
IngressControllerClassName: tt.fields.IngressControllerClassName,
IstioVirtualServiceEnabled: tt.fields.IstioVirtualServiceEnabled,
}
got, got1, err := r.generateVirtualService(tt.args.ctx, tt.args.opt)
if (err != nil) != tt.wantErr {
t.Errorf("DynamoNimDeploymentReconciler.generateVirtualService() error = %v, wantErr %v", err, tt.wantErr)
return
}
g.Expect(got).To(gomega.Equal(tt.want))
g.Expect(got1).To(gomega.Equal(tt.want1))
})
}
}
......@@ -256,7 +256,6 @@ func GenerateDynamoNIMDeployments(ctx context.Context, parentDynamoDeployment *v
if config.EntryService == service.Name {
// enable virtual service for the entry service
deployment.Spec.Ingress.Enabled = true
deployment.Spec.Ingress.UseVirtualService = &deployment.Spec.Ingress.Enabled
}
}
if service.Config.Resources != nil {
......
......@@ -228,8 +228,7 @@ func TestGenerateDynamoNIMDeployments(t *testing.T) {
},
},
Ingress: v1alpha1.IngressSpec{
Enabled: true,
UseVirtualService: &[]bool{true}[0],
Enabled: true,
},
},
},
......@@ -336,8 +335,7 @@ func TestGenerateDynamoNIMDeployments(t *testing.T) {
},
},
Ingress: v1alpha1.IngressSpec{
Enabled: true,
UseVirtualService: &[]bool{true}[0],
Enabled: true,
},
},
},
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment