diff --git a/.gitignore b/.gitignore index 4029db26..87128dc3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ coverage.out .idea/* *.xml -.env \ No newline at end of file +.env +bin/* \ No newline at end of file diff --git a/Makefile b/Makefile index ebb643a1..9f3db4ae 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,71 @@ -.PHONY: clean dev push push-tester-image e2e run-e2e +# Run `make help` for usage information on commands in this file. + +.PHONY: help clean dev push e2e unit crd manifests generate controller-gen -include .env -# can have values of "public" or "private" -CLUSTER_TYPE="public" +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +CONTROLLER_TOOLS_VERSION ?= v0.13.0 + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +help: ## Display this help. + # prints all targets with comments next to them, extracted from this file + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) -clean: +clean: ## Cleans the development environment state rm -rf devenv/state devenv/tf/.terraform.lock.hcl devenv/tf/.terraform devenv/tf/terraform.tfstate devenv/tf/terraform.tfstate.backup -dev: +# can have values of "public" or "private" +CLUSTER_TYPE="public" + +dev: clean ## Deploys a development environment useful for testing the operator inside a cluster terraform --version cd devenv && mkdir -p state && cd tf && terraform init && terraform apply -auto-approve -var="clustertype=$(CLUSTER_TYPE)" ./devenv/scripts/deploy_operator.sh -push: +push: ## Pushes the current operator code to the current development environment echo "$(shell cat devenv/state/registry.txt)/app-routing-operator:$(shell date +%s)" > devenv/state/operator-image-tag.txt az acr login -n `cat devenv/state/registry.txt` docker build -t `cat devenv/state/operator-image-tag.txt` . docker push `cat devenv/state/operator-image-tag.txt` ./devenv/scripts/push_image.sh -e2e: +e2e: ## Runs end-to-end tests # parenthesis preserve current working directory (cd testing/e2e && \ go run ./main.go infra --subscription=${SUBSCRIPTION_ID} --tenant=${TENANT_ID} --names=${INFRA_NAMES} && \ go run ./main.go deploy) -unit: +unit: ## Runs unit tests docker build ./devenv/ -t app-routing-dev:latest docker run --rm -v "$(shell pwd)":/usr/src/project -w /usr/src/project app-routing-dev:latest go test ./... +crd: generate manifests ## Generates all associated files from CRD + +manifests: controller-gen ## Generate CRD manifest + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./api/..." output:crd:artifacts:config=config/crd/bases + +generate: $(CONTROLLER_GEN) ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object paths="./api/..." + + +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. +$(CONTROLLER_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) \ No newline at end of file diff --git a/PROJECT b/PROJECT new file mode 100644 index 00000000..6e2d8acf --- /dev/null +++ b/PROJECT @@ -0,0 +1,20 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +domain: kubernetes.azure.com +layout: +- go.kubebuilder.io/v4 +projectName: app-routing +repo: github.com/Azure/aks-app-routing-operator +resources: +- api: + crdVersion: v1 + namespaced: false + controller: true + domain: kubernetes.azure.com + group: approuting + kind: NginxIngressController + path: github.com/Azure/aks-app-routing-operator/api/v1alpha1 + version: v1alpha1 +version: "3" diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go new file mode 100644 index 00000000..68c0beee --- /dev/null +++ b/api/v1alpha1/groupversion_info.go @@ -0,0 +1,20 @@ +// Package v1alpha1 contains API Schema definitions for the approuting v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=approuting.kubernetes.azure.com +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "approuting.kubernetes.azure.com", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/v1alpha1/nginxingresscontroller_types.go b/api/v1alpha1/nginxingresscontroller_types.go new file mode 100644 index 00000000..bd054c9a --- /dev/null +++ b/api/v1alpha1/nginxingresscontroller_types.go @@ -0,0 +1,188 @@ +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Important: Run "make crd" to regenerate code after modifying this file +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// NginxIngressControllerSpec defines the desired state of NginxIngressController +type NginxIngressControllerSpec struct { + // IngressClassName is the name of the IngressClass that will be used for the NGINX Ingress Controller. Defaults to metadata.name if + // not specified. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + IngressClassName string `json:"ingressClassName,omitempty"` + + // ControllerNamePrefix is the name to use for the managed NGINX Ingress Controller resources. + // +optional + // +kubebuilder:default=nginx + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + ControllerNamePrefix string `json:"controllerNamePrefix,omitempty"` + + // LoadBalancerAnnotations is a map of annotations to apply to the NGINX Ingress Controller's Service. Common annotations + // will be from the Azure LoadBalancer annotations here https://cloud-provider-azure.sigs.k8s.io/topics/loadbalancer/#loadbalancer-annotations + // +optional + LoadBalancerAnnotations map[string]string `json:"loadBalancerAnnotations,omitempty"` +} + +// NginxIngressControllerStatus defines the observed state of NginxIngressController +type NginxIngressControllerStatus struct { + // Conditions is an array of current observed conditions for the NGINX Ingress Controller + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions"` + + // ControllerReplicas is the desired number of replicas of the NGINX Ingress Controller + // +optional + ControllerReplicas int32 `json:"controllerReplicas"` + + // ControllerReadyReplicas is the number of ready replicas of the NGINX Ingress Controller deployment + // +optional + ControllerReadyReplicas int32 `json:"controllerReadyReplicas"` + + // ControllerAvailableReplicas is the number of available replicas of the NGINX Ingress Controller deployment + // +optional + ControllerAvailableReplicas int32 `json:"controllerAvailableReplicas"` + + // ControllerUnavailableReplicas is the number of unavailable replicas of the NGINX Ingress Controller deployment + // +optional + ControllerUnavailableReplicas int32 `json:"controllerUnavailableReplicas"` + + // Count of hash collisions for the managed resources. The App Routing Operator uses this field + // as a collision avoidance mechanism when it needs to create the name for the managed resources. + // +optional + CollisionCount int32 `json:"collisionCount"` + + // ManagedResourceRefs is a list of references to the managed resources + // +optional + ManagedResourceRefs []ManagedObjectReference `json:"managedResourceRefs,omitempty"` +} + +const ( + // ConditionTypeAvailable indicates whether the NGINX Ingress Controller is available. Its condition status is one of + // - "True" when the NGINX Ingress Controller is available and can be used + // - "False" when the NGINX Ingress Controller is not available and cannot offer full functionality + // - "Unknown" when the NGINX Ingress Controller's availability cannot be determined + ConditionTypeAvailable = "Available" + + // ConditionTypeIngressClassReady indicates whether the IngressClass exists. Its condition status is one of + // - "True" when the IngressClass exists + // - "False" when the IngressClass does not exist + // - "Collision" when the IngressClass exists, but it's not owned by the NginxIngressController. + // - "Unknown" when the IngressClass's existence cannot be determined + ConditionTypeIngressClassReady = "IngressClassReady" + + // ConditionTypeControllerAvailable indicates whether the NGINX Ingress Controller deployment is available. Its condition status is one of + // - "True" when the NGINX Ingress Controller deployment is available + // - "False" when the NGINX Ingress Controller deployment is not available + // - "Unknown" when the NGINX Ingress Controller deployment's availability cannot be determined + ConditionTypeControllerAvailable = "ControllerAvailable" + + // ConditionTypeProgressing indicates whether the NGINX Ingress Controller availability is progressing. Its condition status is one of + // - "True" when the NGINX Ingress Controller availability is progressing + // - "False" when the NGINX Ingress Controller availability is not progressing + // - "Unknown" when the NGINX Ingress Controller availability's progress cannot be determined + ConditionTypeProgressing = "Progressing" +) + +// ManagedObjectReference is a reference to an object +type ManagedObjectReference struct { + // Name is the name of the managed object + Name string `json:"name"` + + // Namespace is the namespace of the managed object. If not specified, the resource is cluster-scoped + // +optional + Namespace string `json:"namespace"` + + // Kind is the kind of the managed object + Kind string `json:"kind"` + + // APIGroup is the API group of the managed object. If not specified, the resource is in the core API group + // +optional + APIGroup string `json:"apiGroup"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Cluster,shortName=nic +//+kubebuilder:printcolumn:name="IngressClass",type="string",JSONPath=`.spec.ingressClassName` +//+kubebuilder:printcolumn:name="ControllerNamePrefix",type="string",JSONPath=`.spec.controllerNamePrefix` +//+kubebuilder:printcolumn:name="Available",type="string",JSONPath=`.status.conditions[?(@.type=="Available")].status` + +// NginxIngressController is the Schema for the nginxingresscontrollers API +type NginxIngressController struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +required + Spec NginxIngressControllerSpec `json:"spec,omitempty"` + + // +optional + Status NginxIngressControllerStatus `json:"status,omitempty"` +} + +func (n *NginxIngressController) GetCondition(t string) *metav1.Condition { + return meta.FindStatusCondition(n.Status.Conditions, t) +} + +func (n *NginxIngressController) SetCondition(c metav1.Condition) { + current := n.GetCondition(c.Type) + + if current != nil && current.Status == c.Status && current.Message == c.Message && current.Reason == c.Reason { + current.ObservedGeneration = n.Generation + return + } + + c.ObservedGeneration = n.Generation + c.LastTransitionTime = metav1.Now() + meta.SetStatusCondition(&n.Status.Conditions, c) +} + +// Valid checks this NginxIngressController to see if it's valid. Returns a string describing the validation error, if any, or empty string if there is no error. +func (n *NginxIngressController) Valid() string { + if n.Spec.ControllerNamePrefix == "" { + return "spec.controllerNamePrefix must be specified" + } + + if n.Spec.IngressClassName == "" { + return "spec.ingressClassName must be specified" + } + + if len(n.Name) > 100 { + return "Name length must be less than or equal to 100 characters" + } + + // TODO: add more specific validations https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + + return "" +} + +func (n *NginxIngressController) Default() { + if n.Spec.IngressClassName == "" { + n.Spec.IngressClassName = n.Name + } + + if n.Spec.ControllerNamePrefix == "" { + n.Spec.ControllerNamePrefix = "nginx" + } +} + +//+kubebuilder:object:root=true +//+kubebuilder:resource:scope=Cluster + +// NginxIngressControllerList contains a list of NginxIngressController +type NginxIngressControllerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NginxIngressController `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NginxIngressController{}, &NginxIngressControllerList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000..4b7e070e --- /dev/null +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,133 @@ +//go:build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManagedObjectReference) DeepCopyInto(out *ManagedObjectReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedObjectReference. +func (in *ManagedObjectReference) DeepCopy() *ManagedObjectReference { + if in == nil { + return nil + } + out := new(ManagedObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NginxIngressController) DeepCopyInto(out *NginxIngressController) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxIngressController. +func (in *NginxIngressController) DeepCopy() *NginxIngressController { + if in == nil { + return nil + } + out := new(NginxIngressController) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NginxIngressController) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NginxIngressControllerList) DeepCopyInto(out *NginxIngressControllerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NginxIngressController, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxIngressControllerList. +func (in *NginxIngressControllerList) DeepCopy() *NginxIngressControllerList { + if in == nil { + return nil + } + out := new(NginxIngressControllerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NginxIngressControllerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NginxIngressControllerSpec) DeepCopyInto(out *NginxIngressControllerSpec) { + *out = *in + if in.LoadBalancerAnnotations != nil { + in, out := &in.LoadBalancerAnnotations, &out.LoadBalancerAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxIngressControllerSpec. +func (in *NginxIngressControllerSpec) DeepCopy() *NginxIngressControllerSpec { + if in == nil { + return nil + } + out := new(NginxIngressControllerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NginxIngressControllerStatus) DeepCopyInto(out *NginxIngressControllerStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ManagedResourceRefs != nil { + in, out := &in.ManagedResourceRefs, &out.ManagedResourceRefs + *out = make([]ManagedObjectReference, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxIngressControllerStatus. +func (in *NginxIngressControllerStatus) DeepCopy() *NginxIngressControllerStatus { + if in == nil { + return nil + } + out := new(NginxIngressControllerStatus) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/approuting.kubernetes.azure.com_nginxingresscontrollers.yaml b/config/crd/bases/approuting.kubernetes.azure.com_nginxingresscontrollers.yaml new file mode 100644 index 00000000..e9d38fa3 --- /dev/null +++ b/config/crd/bases/approuting.kubernetes.azure.com_nginxingresscontrollers.yaml @@ -0,0 +1,207 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: nginxingresscontrollers.approuting.kubernetes.azure.com +spec: + group: approuting.kubernetes.azure.com + names: + kind: NginxIngressController + listKind: NginxIngressControllerList + plural: nginxingresscontrollers + shortNames: + - nic + singular: nginxingresscontroller + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.ingressClassName + name: IngressClass + type: string + - jsonPath: .spec.controllerNamePrefix + name: ControllerNamePrefix + type: string + - jsonPath: .status.conditions[?(@.type=="Available")].status + name: Available + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: NginxIngressController is the Schema for the nginxingresscontrollers + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: NginxIngressControllerSpec defines the desired state of NginxIngressController + properties: + controllerNamePrefix: + default: nginx + description: ControllerNamePrefix is the name to use for the managed + NGINX Ingress Controller resources. + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + ingressClassName: + description: IngressClassName is the name of the IngressClass that + will be used for the NGINX Ingress Controller. Defaults to metadata.name + if not specified. + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + loadBalancerAnnotations: + additionalProperties: + type: string + description: LoadBalancerAnnotations is a map of annotations to apply + to the NGINX Ingress Controller's Service. Common annotations will + be from the Azure LoadBalancer annotations here https://cloud-provider-azure.sigs.k8s.io/topics/loadbalancer/#loadbalancer-annotations + type: object + type: object + status: + description: NginxIngressControllerStatus defines the observed state of + NginxIngressController + properties: + collisionCount: + description: Count of hash collisions for the managed resources. The + App Routing Operator uses this field as a collision avoidance mechanism + when it needs to create the name for the managed resources. + format: int32 + type: integer + conditions: + description: Conditions is an array of current observed conditions + for the NGINX Ingress Controller + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerAvailableReplicas: + description: ControllerAvailableReplicas is the number of available + replicas of the NGINX Ingress Controller deployment + format: int32 + type: integer + controllerReadyReplicas: + description: ControllerReadyReplicas is the number of ready replicas + of the NGINX Ingress Controller deployment + format: int32 + type: integer + controllerReplicas: + description: ControllerReplicas is the desired number of replicas + of the NGINX Ingress Controller + format: int32 + type: integer + controllerUnavailableReplicas: + description: ControllerUnavailableReplicas is the number of unavailable + replicas of the NGINX Ingress Controller deployment + format: int32 + type: integer + managedResourceRefs: + description: ManagedResourceRefs is a list of references to the managed + resources + items: + description: ManagedObjectReference is a reference to an object + properties: + apiGroup: + description: APIGroup is the API group of the managed object. + If not specified, the resource is in the core API group + type: string + kind: + description: Kind is the kind of the managed object + type: string + name: + description: Name is the name of the managed object + type: string + namespace: + description: Namespace is the namespace of the managed object. + If not specified, the resource is cluster-scoped + type: string + required: + - kind + - name + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {}