diff --git a/deployments/common/crds-v1beta1/k8s.nginx.org_transportservers.yaml b/deployments/common/crds-v1beta1/k8s.nginx.org_transportservers.yaml index 714413b849..39ea4dbe85 100644 --- a/deployments/common/crds-v1beta1/k8s.nginx.org_transportservers.yaml +++ b/deployments/common/crds-v1beta1/k8s.nginx.org_transportservers.yaml @@ -6,6 +6,17 @@ metadata: creationTimestamp: null name: transportservers.k8s.nginx.org spec: + additionalPrinterColumns: + - JSONPath: .status.state + description: Current state of the VirtualServer. If the resource has a valid status, it means it has been validated and accepted by the Ingress Controller. + name: State + type: string + - JSONPath: .status.reason + name: Reason + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date group: k8s.nginx.org names: kind: TransportServer @@ -16,6 +27,8 @@ spec: singular: transportserver preserveUnknownFields: false scope: Namespaced + subresources: + status: {} validation: openAPIV3Schema: description: TransportServer defines the TransportServer resource. @@ -107,6 +120,16 @@ spec: type: integer service: type: string + status: + description: TransportServerStatus defines the status for the TransportServer resource. + type: object + properties: + message: + type: string + reason: + type: string + state: + type: string version: v1alpha1 versions: - name: v1alpha1 diff --git a/deployments/common/crds/k8s.nginx.org_transportservers.yaml b/deployments/common/crds/k8s.nginx.org_transportservers.yaml index 3d480307ab..16bedd760b 100644 --- a/deployments/common/crds/k8s.nginx.org_transportservers.yaml +++ b/deployments/common/crds/k8s.nginx.org_transportservers.yaml @@ -16,7 +16,18 @@ spec: singular: transportserver scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: Current state of the VirtualServer. If the resource has a valid status, it means it has been validated and accepted by the Ingress Controller. + jsonPath: .status.state + name: State + type: string + - jsonPath: .status.reason + name: Reason + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: description: TransportServer defines the TransportServer resource. @@ -108,8 +119,20 @@ spec: type: integer service: type: string + status: + description: TransportServerStatus defines the status for the TransportServer resource. + type: object + properties: + message: + type: string + reason: + type: string + state: + type: string served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/deployments/helm-chart/crds/k8s.nginx.org_transportservers.yaml b/deployments/helm-chart/crds/k8s.nginx.org_transportservers.yaml index 3d480307ab..16bedd760b 100644 --- a/deployments/helm-chart/crds/k8s.nginx.org_transportservers.yaml +++ b/deployments/helm-chart/crds/k8s.nginx.org_transportservers.yaml @@ -16,7 +16,18 @@ spec: singular: transportserver scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: Current state of the VirtualServer. If the resource has a valid status, it means it has been validated and accepted by the Ingress Controller. + jsonPath: .status.state + name: State + type: string + - jsonPath: .status.reason + name: Reason + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: description: TransportServer defines the TransportServer resource. @@ -108,8 +119,20 @@ spec: type: integer service: type: string + status: + description: TransportServerStatus defines the status for the TransportServer resource. + type: object + properties: + message: + type: string + reason: + type: string + state: + type: string served: true storage: true + subresources: + status: {} status: acceptedNames: kind: "" diff --git a/docs-web/configuration/global-configuration/reporting-resources-status.md b/docs-web/configuration/global-configuration/reporting-resources-status.md index f05a4f29ed..556afb1f29 100644 --- a/docs-web/configuration/global-configuration/reporting-resources-status.md +++ b/docs-web/configuration/global-configuration/reporting-resources-status.md @@ -115,7 +115,7 @@ Notes: The Ingress controller does not clear the status of VirtualServer and Vir ## Policy Resources A Policy resource includes the status field with information about the state of the resource. -You can see the status in the ouput of the `kubectl get policy` command as shown below: +You can see the status in the output of the `kubectl get policy` command as shown below: ``` $ kubectl get policy NAME STATE AGE @@ -151,3 +151,45 @@ The following fields are reported in Policy status: - Additional information about the state. - ``string`` ``` + + +## TransportServer Resources + +A TransportServer resource includes the status field with information about the state of the resource. +You can see the status in the output of the `kubectl get transportserver` command as shown below: +``` +$ kubectl get transportserver + NAME STATE REASON AGE + dns-tcp Valid AddedOrUpdated 47m +``` +In order to see additional addresses or extra information about the `Status` of the resource, use the following command: +``` +$ kubectl describe transportserver +. . . +Status: + Message: Configuration for default/dns-tcp was added or updated + Reason: AddedOrUpdated + State: Valid +``` + +### Status Specification +The following fields are reported in TransportServer status: + +```eval_rst +.. list-table:: + :header-rows: 1 + + * - Field + - Description + - Type + * - ``State`` + - Current state of the resource. Can be ``Valid`` or ``Invalid``. For more information, refer to the ``message`` field. + - ``string`` + * - ``Reason`` + - The reason of the last update. + - ``string`` + * - ``Message`` + - Additional information about the state. + - ``string`` +``` + diff --git a/internal/configs/configurator.go b/internal/configs/configurator.go index c459c36212..ff4ceba418 100644 --- a/internal/configs/configurator.go +++ b/internal/configs/configurator.go @@ -1182,16 +1182,6 @@ func (cnf *Configurator) GetVirtualServerCounts() (vsCount int, vsrCount int) { return vsCount, vsrCount } -func (cnf *Configurator) CheckIfListenerExists(transportServerListener *conf_v1alpha1.TransportServerListener) bool { - listener, exists := cnf.globalCfgParams.Listeners[transportServerListener.Name] - - if !exists { - return false - } - - return transportServerListener.Protocol == listener.Protocol -} - // AddOrUpdateSpiffeCerts writes Spiffe certs and keys to disk and reloads NGINX func (cnf *Configurator) AddOrUpdateSpiffeCerts(svidResponse *workload.X509SVIDs) error { svid := svidResponse.Default() diff --git a/internal/configs/configurator_test.go b/internal/configs/configurator_test.go index 92cff0ad27..228a9f31e3 100644 --- a/internal/configs/configurator_test.go +++ b/internal/configs/configurator_test.go @@ -234,58 +234,6 @@ func TestGetFileNameForVirtualServerFromKey(t *testing.T) { } } -func TestCheckIfListenerExists(t *testing.T) { - tests := []struct { - listener conf_v1alpha1.TransportServerListener - expected bool - msg string - }{ - { - listener: conf_v1alpha1.TransportServerListener{ - Name: "tcp-listener", - Protocol: "TCP", - }, - expected: true, - msg: "name and protocol match", - }, - { - listener: conf_v1alpha1.TransportServerListener{ - Name: "some-listener", - Protocol: "TCP", - }, - expected: false, - msg: "only protocol matches", - }, - { - listener: conf_v1alpha1.TransportServerListener{ - Name: "tcp-listener", - Protocol: "UDP", - }, - expected: false, - msg: "only name matches", - }, - } - - cnf, err := createTestConfigurator() - if err != nil { - t.Errorf("Failed to create a test configurator: %v", err) - } - - cnf.globalCfgParams.Listeners = map[string]Listener{ - "tcp-listener": { - Port: 53, - Protocol: "TCP", - }, - } - - for _, test := range tests { - result := cnf.CheckIfListenerExists(&test.listener) - if result != test.expected { - t.Errorf("CheckIfListenerExists() returned %v but expected %v for the case of %q", result, test.expected, test.msg) - } - } -} - func TestGetFileNameForTransportServer(t *testing.T) { transportServer := &conf_v1alpha1.TransportServer{ ObjectMeta: meta_v1.ObjectMeta{ diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index ccf1ea504f..24eb4ae8ed 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -297,6 +297,7 @@ func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalanc ingressLister: &lbc.ingressLister, virtualServerLister: lbc.virtualServerLister, virtualServerRouteLister: lbc.virtualServerRouteLister, + transportServerLister: lbc.transportServerLister, policyLister: lbc.policyLister, keyFunc: keyFunc, confClient: input.ConfClient, @@ -865,11 +866,9 @@ func (lbc *LoadBalancerController) syncTransportServer(task task) { if !tsExists { glog.V(2).Infof("Deleting TransportServer: %v\n", key) - changes, problems = lbc.configuration.DeleteTransportServer(key) } else { glog.V(2).Infof("Adding or Updating TransportServer: %v\n", key) - ts := obj.(*conf_v1alpha1.TransportServer) changes, problems = lbc.configuration.AddOrUpdateTransportServer(ts) } @@ -977,14 +976,17 @@ func (lbc *LoadBalancerController) processProblems(problems []ConfigurationProbl if err != nil { glog.Errorf("Error when updating the status for VirtualServer %v/%v: %v", obj.Namespace, obj.Name, err) } + case *conf_v1alpha1.TransportServer: + err := lbc.statusUpdater.UpdateTransportServerStatus(obj, state, p.Reason, p.Message) + if err != nil { + glog.Errorf("Error when updating the status for TransportServer %v/%v: %v", obj.Namespace, obj.Name, err) + } case *conf_v1.VirtualServerRoute: var emptyVSes []*conf_v1.VirtualServer err := lbc.statusUpdater.UpdateVirtualServerRouteStatusWithReferencedBy(obj, state, p.Reason, p.Message, emptyVSes) if err != nil { glog.Errorf("Error when updating the status for VirtualServerRoute %v/%v: %v", obj.Namespace, obj.Name, err) } - case *conf_v1alpha1.TransportServer: - // do nothing until the TransportServer supports status } } } @@ -1018,7 +1020,7 @@ func (lbc *LoadBalancerController) processChanges(changes []ResourceChange) { tsEx := lbc.createTransportServerEx(impl.TransportServer, impl.ListenerPort) addOrUpdateErr := lbc.configurator.AddOrUpdateTransportServer(tsEx) - lbc.updateTransportServerEvents(impl, addOrUpdateErr) + lbc.updateTransportServerStatusAndEvents(impl, addOrUpdateErr) } } else if c.Op == Delete { switch impl := c.Resource.(type) { @@ -1346,7 +1348,7 @@ func (lbc *LoadBalancerController) updateResourcesStatusAndEvents(resources []Re lbc.updateRegularIngressStatusAndEvents(impl, warnings, operationErr) } case *TransportServerConfiguration: - lbc.updateTransportServerEvents(impl, operationErr) + lbc.updateTransportServerStatusAndEvents(impl, operationErr) } } } @@ -1453,25 +1455,35 @@ func (lbc *LoadBalancerController) updateRegularIngressStatusAndEvents(ingConfig } } -func (lbc *LoadBalancerController) updateTransportServerEvents(tsConfig *TransportServerConfiguration, operationErr error) { +func (lbc *LoadBalancerController) updateTransportServerStatusAndEvents(tsConfig *TransportServerConfiguration, operationErr error) { eventTitle := "AddedOrUpdated" eventType := api_v1.EventTypeNormal eventWarningMessage := "" + state := conf_v1.StateValid if len(tsConfig.Warnings) > 0 { eventType = api_v1.EventTypeWarning eventTitle = "AddedOrUpdatedWithWarning" eventWarningMessage = fmt.Sprintf("with warning(s): %s", formatWarningMessages(tsConfig.Warnings)) + state = conf_v1.StateWarning } if operationErr != nil { eventType = api_v1.EventTypeWarning eventTitle = "AddedOrUpdatedWithError" eventWarningMessage = fmt.Sprintf("%s; but was not applied: %v", eventWarningMessage, operationErr) + state = conf_v1.StateInvalid } msg := fmt.Sprintf("Configuration for %v was added or updated %s", getResourceKey(&tsConfig.TransportServer.ObjectMeta), eventWarningMessage) lbc.recorder.Eventf(tsConfig.TransportServer, eventType, eventTitle, msg) + + if lbc.reportCustomResourceStatusEnabled() { + err := lbc.statusUpdater.UpdateTransportServerStatus(tsConfig.TransportServer, state, eventTitle, msg) + if err != nil { + glog.Errorf("Error when updating the status for TransportServer %v/%v: %v", tsConfig.TransportServer.Namespace, tsConfig.TransportServer.Name, err) + } + } } func (lbc *LoadBalancerController) updateVirtualServerStatusAndEvents(vsConfig *VirtualServerConfiguration, warnings configs.Warnings, operationErr error) { @@ -1958,6 +1970,43 @@ func (lbc *LoadBalancerController) updatePoliciesStatus() error { return nil } +func (lbc *LoadBalancerController) updateTransportServersStatusFromEvents() error { + var allErrs []error + for _, obj := range lbc.transportServerLister.List() { + ts := obj.(*conf_v1alpha1.TransportServer) + + events, err := lbc.client.CoreV1().Events(ts.Namespace).List(context.TODO(), + meta_v1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.name=%v,involvedObject.uid=%v", ts.Name, ts.UID)}) + if err != nil { + allErrs = append(allErrs, fmt.Errorf("error trying to get events for TransportServer %v/%v: %v", ts.Namespace, ts.Name, err)) + break + } + + if len(events.Items) == 0 { + continue + } + + var timestamp time.Time + var latestEvent api_v1.Event + for _, event := range events.Items { + if event.CreationTimestamp.After(timestamp) { + latestEvent = event + } + } + + err = lbc.statusUpdater.UpdateTransportServerStatus(ts, getStatusFromEventTitle(latestEvent.Reason), latestEvent.Reason, latestEvent.Message) + if err != nil { + allErrs = append(allErrs, err) + } + } + + if len(allErrs) > 0 { + return fmt.Errorf("not all TransportServers statuses were updated: %v", allErrs) + } + + return nil +} + func getIPAddressesFromEndpoints(endpoints []podEndpoint) []string { var endps []string for _, ep := range endpoints { diff --git a/internal/k8s/leader.go b/internal/k8s/leader.go index 575dfac464..d82bc0578e 100644 --- a/internal/k8s/leader.go +++ b/internal/k8s/leader.go @@ -78,6 +78,11 @@ func createLeaderHandler(lbc *LoadBalancerController) leaderelection.LeaderCallb if err != nil { glog.V(3).Infof("error updating Policies status when starting leading: %v", err) } + + err = lbc.updateTransportServersStatusFromEvents() + if err != nil { + glog.V(3).Infof("error updating TransportServers status when starting leading: %v", err) + } } }, OnStoppedLeading: func() { diff --git a/internal/k8s/status.go b/internal/k8s/status.go index 2c1779814c..24d4f96606 100644 --- a/internal/k8s/status.go +++ b/internal/k8s/status.go @@ -11,6 +11,7 @@ import ( "github.com/golang/glog" conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" k8s_nginx "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned" api_v1 "k8s.io/api/core/v1" networking "k8s.io/api/networking/v1beta1" @@ -40,6 +41,7 @@ type statusUpdater struct { ingressLister *storeToIngressLister virtualServerLister cache.Store virtualServerRouteLister cache.Store + transportServerLister cache.Store policyLister cache.Store confClient k8s_nginx.Interface } @@ -332,6 +334,21 @@ func (su *statusUpdater) ClearStatusFromIngressLink() { su.externalEndpoints = su.generateExternalEndpointsFromStatus(su.status) } +func (su *statusUpdater) retryUpdateTransportServerStatus(tsCopy *conf_v1alpha1.TransportServer) error { + ts, err := su.confClient.K8sV1alpha1().TransportServers(tsCopy.Namespace).Get(context.TODO(), tsCopy.Name, metav1.GetOptions{}) + if err != nil { + return err + } + + ts.Status = tsCopy.Status + _, err = su.confClient.K8sV1alpha1().TransportServers(ts.Namespace).UpdateStatus(context.TODO(), ts, metav1.UpdateOptions{}) + if err != nil { + return err + } + + return nil +} + func (su *statusUpdater) retryUpdateVirtualServerStatus(vsCopy *conf_v1.VirtualServer) error { vs, err := su.confClient.K8sV1().VirtualServers(vsCopy.Namespace).Get(context.TODO(), vsCopy.Name, metav1.GetOptions{}) if err != nil { @@ -378,6 +395,48 @@ func hasVsStatusChanged(vs *conf_v1.VirtualServer, state string, reason string, return false } +// UpdateTransportServerStatus updates the status of a TransportServer. +func (su *statusUpdater) UpdateTransportServerStatus(ts *conf_v1alpha1.TransportServer, state string, reason string, message string) error { + tsLatest, exists, err := su.transportServerLister.Get(ts) + if err != nil { + glog.V(3).Infof("error getting TransportServer from Store: %v", err) + return err + } + if !exists { + glog.V(3).Infof("TransportServer doesn't exist in Store") + return nil + } + + if !hasTsStatusChanged(tsLatest.(*conf_v1alpha1.TransportServer), state, reason, message) { + return nil + } + + tsCopy := tsLatest.(*conf_v1alpha1.TransportServer).DeepCopy() + tsCopy.Status.State = state + tsCopy.Status.Reason = reason + tsCopy.Status.Message = message + + _, err = su.confClient.K8sV1alpha1().TransportServers(tsCopy.Namespace).UpdateStatus(context.TODO(), tsCopy, metav1.UpdateOptions{}) + if err != nil { + glog.V(3).Infof("error setting TransportServer %v/%v status, retrying: %v", tsCopy.Namespace, tsCopy.Name, err) + return su.retryUpdateTransportServerStatus(tsCopy) + } + return err +} + +func hasTsStatusChanged(ts *conf_v1alpha1.TransportServer, state string, reason string, message string) bool { + if ts.Status.State != state { + return true + } + if ts.Status.Reason != reason { + return true + } + if ts.Status.Message != message { + return true + } + return false +} + // UpdateVirtualServerStatus updates the status of a VirtualServer. func (su *statusUpdater) UpdateVirtualServerStatus(vs *conf_v1.VirtualServer, state string, reason string, message string) error { // Get an up-to-date VirtualServer from the Store diff --git a/internal/k8s/status_test.go b/internal/k8s/status_test.go index 3d17f3ad59..8c70acd82f 100644 --- a/internal/k8s/status_test.go +++ b/internal/k8s/status_test.go @@ -5,7 +5,10 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" + conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" + fake_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/fake" v1 "k8s.io/api/core/v1" networking "k8s.io/api/networking/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -15,6 +18,164 @@ import ( "k8s.io/client-go/tools/cache" ) +func TestUpdateTransportServerStatus(t *testing.T) { + ts := &conf_v1alpha1.TransportServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "ts-1", + Namespace: "default", + }, + Status: conf_v1alpha1.TransportServerStatus{ + State: "before status", + Reason: "before reason", + Message: "before message", + }, + } + + fakeClient := fake_v1alpha1.NewSimpleClientset( + &conf_v1alpha1.TransportServerList{ + Items: []conf_v1alpha1.TransportServer{ + *ts, + }}) + + tsLister := cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc) + + err := tsLister.Add(ts) + if err != nil { + t.Errorf("Error adding TransportServer to the transportserver lister: %v", err) + } + su := statusUpdater{ + transportServerLister: tsLister, + confClient: fakeClient, + keyFunc: cache.DeletionHandlingMetaNamespaceKeyFunc, + } + + err = su.UpdateTransportServerStatus(ts, "after status", "after reason", "after message") + if err != nil { + t.Errorf("error updating transportserver status: %v", err) + } + updatedTs, _ := fakeClient.K8sV1alpha1().TransportServers(ts.Namespace).Get(context.TODO(), ts.Name, meta_v1.GetOptions{}) + + expectedStatus := conf_v1alpha1.TransportServerStatus{ + State: "after status", + Reason: "after reason", + Message: "after message", + } + + if diff := cmp.Diff(expectedStatus, updatedTs.Status); diff != "" { + t.Errorf("Unexpected status (-want +got):\n%s", diff) + } +} + +func TestUpdateTransportServerStatusIgnoreNoChange(t *testing.T) { + ts := &conf_v1alpha1.TransportServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "ts-1", + Namespace: "default", + }, + Status: conf_v1alpha1.TransportServerStatus{ + State: "same status", + Reason: "same reason", + Message: "same message", + }, + } + + fakeClient := fake_v1alpha1.NewSimpleClientset( + &conf_v1alpha1.TransportServerList{ + Items: []conf_v1alpha1.TransportServer{ + *ts, + }}) + + tsLister, _ := cache.NewInformer( + cache.NewListWatchFromClient( + fakeClient.K8sV1alpha1().RESTClient(), + "transportservers", + "nginx-ingress", + fields.Everything(), + ), + &conf_v1alpha1.TransportServer{}, + 2, + nil, + ) + + err := tsLister.Add(ts) + if err != nil { + t.Errorf("Error adding TransportServer to the transportserver lister: %v", err) + } + su := statusUpdater{ + transportServerLister: tsLister, + confClient: fakeClient, + keyFunc: cache.DeletionHandlingMetaNamespaceKeyFunc, + } + + err = su.UpdateTransportServerStatus(ts, "same status", "same reason", "same message") + if err != nil { + t.Errorf("error updating transportserver status: %v", err) + } + updatedTs, _ := fakeClient.K8sV1alpha1().TransportServers(ts.Namespace).Get(context.TODO(), ts.Name, meta_v1.GetOptions{}) + + if updatedTs.Status.State != "same status" { + t.Errorf("expected: %v actual: %v", "same status", updatedTs.Status.State) + } + if updatedTs.Status.Message != "same message" { + t.Errorf("expected: %v actual: %v", "same message", updatedTs.Status.Message) + } + if updatedTs.Status.Reason != "same reason" { + t.Errorf("expected: %v actual: %v", "same reason", updatedTs.Status.Reason) + } +} + +func TestUpdateTransportServerStatusMissingTransportServer(t *testing.T) { + ts := &conf_v1alpha1.TransportServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "ts-1", + Namespace: "default", + }, + Status: conf_v1alpha1.TransportServerStatus{ + State: "before status", + Reason: "before reason", + Message: "before message", + }, + } + + fakeClient := fake_v1alpha1.NewSimpleClientset( + &conf_v1alpha1.TransportServerList{ + Items: []conf_v1alpha1.TransportServer{}}) + + tsLister, _ := cache.NewInformer( + cache.NewListWatchFromClient( + fakeClient.K8sV1alpha1().RESTClient(), + "transportservers", + "nginx-ingress", + fields.Everything(), + ), + &conf_v1alpha1.TransportServer{}, + 2, + nil, + ) + + su := statusUpdater{ + transportServerLister: tsLister, + confClient: fakeClient, + keyFunc: cache.DeletionHandlingMetaNamespaceKeyFunc, + externalEndpoints: []conf_v1.ExternalEndpoint{ + { + IP: "123.123.123.123", + Ports: "1234", + }, + }, + } + + err := su.UpdateTransportServerStatus(ts, "after status", "after reason", "after message") + if err != nil { + t.Errorf("unexpected error: %v, result should be empty as no matching TransportServer is present", err) + } + + updatedTs, _ := fakeClient.K8sV1alpha1().TransportServers(ts.Namespace).Get(context.TODO(), ts.Name, meta_v1.GetOptions{}) + if updatedTs != nil { + t.Errorf("expected TransportServer Store would be empty as provided TransportServer was not found. Unexpected updated TransportServer: %v", updatedTs) + } +} + func TestStatusUpdateWithExternalStatusAndExternalService(t *testing.T) { ing := networking.Ingress{ ObjectMeta: meta_v1.ObjectMeta{ diff --git a/pkg/apis/configuration/v1alpha1/types.go b/pkg/apis/configuration/v1alpha1/types.go index e1c57efb6d..be0b2b452d 100644 --- a/pkg/apis/configuration/v1alpha1/types.go +++ b/pkg/apis/configuration/v1alpha1/types.go @@ -50,13 +50,18 @@ type GlobalConfigurationList struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:validation:Optional // +kubebuilder:resource:shortName=ts +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="State",type=string,JSONPath=`.status.state`,description="Current state of the VirtualServer. If the resource has a valid status, it means it has been validated and accepted by the Ingress Controller." +// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.reason` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // TransportServer defines the TransportServer resource. type TransportServer struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec TransportServerSpec `json:"spec"` + Spec TransportServerSpec `json:"spec"` + Status TransportServerStatus `json:"status"` } // TransportServerSpec is the spec of the TransportServer resource. @@ -118,6 +123,13 @@ type Action struct { Pass string `json:"pass"` } +// TransportServerStatus defines the status for the TransportServer resource. +type TransportServerStatus struct { + State string `json:"state"` + Reason string `json:"reason"` + Message string `json:"message"` +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // TransportServerList is a list of the TransportServer resources. diff --git a/pkg/apis/configuration/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/configuration/v1alpha1/zz_generated.deepcopy.go index d46d30ec7c..7ed79b9065 100644 --- a/pkg/apis/configuration/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/configuration/v1alpha1/zz_generated.deepcopy.go @@ -159,6 +159,7 @@ func (in *TransportServer) DeepCopyInto(out *TransportServer) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status return } @@ -268,6 +269,22 @@ func (in *TransportServerSpec) DeepCopy() *TransportServerSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TransportServerStatus) DeepCopyInto(out *TransportServerStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransportServerStatus. +func (in *TransportServerStatus) DeepCopy() *TransportServerStatus { + if in == nil { + return nil + } + out := new(TransportServerStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Upstream) DeepCopyInto(out *Upstream) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_transportserver.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_transportserver.go index 08545cc578..735f737e92 100644 --- a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_transportserver.go +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/fake/fake_transportserver.go @@ -86,6 +86,18 @@ func (c *FakeTransportServers) Update(ctx context.Context, transportServer *v1al return obj.(*v1alpha1.TransportServer), err } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeTransportServers) UpdateStatus(ctx context.Context, transportServer *v1alpha1.TransportServer, opts v1.UpdateOptions) (*v1alpha1.TransportServer, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(transportserversResource, "status", c.ns, transportServer), &v1alpha1.TransportServer{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.TransportServer), err +} + // Delete takes name of the transportServer and deletes it. Returns an error if one occurs. func (c *FakeTransportServers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. diff --git a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/transportserver.go b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/transportserver.go index ea3a725ea6..c402b9776d 100644 --- a/pkg/client/clientset/versioned/typed/configuration/v1alpha1/transportserver.go +++ b/pkg/client/clientset/versioned/typed/configuration/v1alpha1/transportserver.go @@ -24,6 +24,7 @@ type TransportServersGetter interface { type TransportServerInterface interface { Create(ctx context.Context, transportServer *v1alpha1.TransportServer, opts v1.CreateOptions) (*v1alpha1.TransportServer, error) Update(ctx context.Context, transportServer *v1alpha1.TransportServer, opts v1.UpdateOptions) (*v1alpha1.TransportServer, error) + UpdateStatus(ctx context.Context, transportServer *v1alpha1.TransportServer, opts v1.UpdateOptions) (*v1alpha1.TransportServer, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.TransportServer, error) @@ -119,6 +120,22 @@ func (c *transportServers) Update(ctx context.Context, transportServer *v1alpha1 return } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *transportServers) UpdateStatus(ctx context.Context, transportServer *v1alpha1.TransportServer, opts v1.UpdateOptions) (result *v1alpha1.TransportServer, err error) { + result = &v1alpha1.TransportServer{} + err = c.client.Put(). + Namespace(c.ns). + Resource("transportservers"). + Name(transportServer.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(transportServer). + Do(ctx). + Into(result) + return +} + // Delete takes name of the transportServer and deletes it. Returns an error if one occurs. func (c *transportServers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { return c.client.Delete().