diff --git a/docs/crd/federation.maistra.io_ExportedServiceSet_v1.adoc b/docs/crd/federation.maistra.io_ExportedServiceSet_v1.adoc index 9962ce16..a3bbf869 100644 --- a/docs/crd/federation.maistra.io_ExportedServiceSet_v1.adoc +++ b/docs/crd/federation.maistra.io_ExportedServiceSet_v1.adoc @@ -60,6 +60,10 @@ maistra.io/api/federation/v1 |=== | Name | Description | Type +| conditions +| Represents the latest available observations of a federation's current state. +| []Condition + | exportedServices | Exports provides details about the services exported by this mesh. | []PeerServiceMapping diff --git a/docs/crd/federation.maistra.io_ImportedServiceSet_v1.adoc b/docs/crd/federation.maistra.io_ImportedServiceSet_v1.adoc index 8ee4dcfd..64c768bb 100644 --- a/docs/crd/federation.maistra.io_ImportedServiceSet_v1.adoc +++ b/docs/crd/federation.maistra.io_ImportedServiceSet_v1.adoc @@ -68,6 +68,10 @@ maistra.io/api/federation/v1 |=== | Name | Description | Type +| conditions +| Represents the latest available observations of a federation's current state. +| []Condition + | importedServices | Imports provides details about the services imported by this mesh. | []PeerServiceMapping diff --git a/docs/crd/federation.maistra.io_ServiceMeshPeer_v1.adoc b/docs/crd/federation.maistra.io_ServiceMeshPeer_v1.adoc index 687b7b9c..7fe4ca7f 100644 --- a/docs/crd/federation.maistra.io_ServiceMeshPeer_v1.adoc +++ b/docs/crd/federation.maistra.io_ServiceMeshPeer_v1.adoc @@ -72,6 +72,10 @@ ServiceMeshPeerStatus provides information related to the other mesh. |=== | Name | Description | Type +| conditions +| Represents the latest available observations of a federation's current state. +| []Condition + | discoveryStatus | DiscoveryStatus represents the discovery status of each pilot/istiod pod in the mesh. | link:federation.maistra.io_ServiceMeshPeer_ServiceMeshPeerDiscoveryStatus_v1.adoc[ServiceMeshPeerDiscoveryStatus] diff --git a/federation/v1/exportedserviceset_types.go b/federation/v1/exportedserviceset_types.go index acebe858..ee2ee79a 100644 --- a/federation/v1/exportedserviceset_types.go +++ b/federation/v1/exportedserviceset_types.go @@ -68,6 +68,7 @@ type ExportedServiceRule struct { } type ExportedServiceSetStatus struct { + StatusConditions `json:",inline"` // Exports provides details about the services exported by this mesh. // +required // +listType=map diff --git a/federation/v1/importedserviceset_types.go b/federation/v1/importedserviceset_types.go index 2f43b11d..5f1eaa55 100644 --- a/federation/v1/importedserviceset_types.go +++ b/federation/v1/importedserviceset_types.go @@ -98,6 +98,7 @@ type ImportedServiceLocality struct { } type ImportedServiceSetStatus struct { + StatusConditions `json:",inline"` // Imports provides details about the services imported by this mesh. // +required // +listType=map diff --git a/federation/v1/servicemeshpeer_types.go b/federation/v1/servicemeshpeer_types.go index 4bd4b43b..0b2f3e70 100644 --- a/federation/v1/servicemeshpeer_types.go +++ b/federation/v1/servicemeshpeer_types.go @@ -120,6 +120,7 @@ type ServiceMeshPeerRemote struct { // ServiceMeshPeerStatus provides information related to the other mesh. type ServiceMeshPeerStatus struct { + StatusConditions `json:",inline"` // DiscoveryStatus represents the discovery status of each pilot/istiod pod // in the mesh. // +optional diff --git a/federation/v1/statusconditions.go b/federation/v1/statusconditions.go new file mode 100644 index 00000000..187c96ef --- /dev/null +++ b/federation/v1/statusconditions.go @@ -0,0 +1,142 @@ +// Copyright Red Hat, Inc. +// +// 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 v1 + +import ( + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type StatusConditions struct { + // Represents the latest available observations of a federation's current state. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,10,rep,name=conditions"` +} + +type ConditionType string + +const ( + // Connected indicates that one or more instances of istiod are currently + // connected to the remote mesh. + ConnectedServiceMeshPeerCondition ConditionType = "Connected" + // Degraded indicates that one or more instances of istiod are currently + // not connected to the remote mesh. + DegradedServiceMeshPeerCondition ConditionType = "Degraded" + // Serving indicates that one or more instances of istiod is currently + // serving discovery information to a remote mesh. + ServingServiceMeshPeerCondition ConditionType = "Degraded" + // Ready indicates that all instances of istiod are connected to the remote + // mesh. + ReadyServiceMeshPeerCondition ConditionType = "Ready" + // Exporting indicates that the mesh is exporting services to the remote + // mesh. + ExportingExportedServiceSetCondition ConditionType = "Exporting" + // Importing indicates that the mesh is importing services from the remote + // mesh. + ImportingImportedServiceSetCondition ConditionType = "Importing" +) + +const ( + ConnectedConditionReason = "Connected" + NotConnectedConditionReason = "NotConnected" + DegradedConditionReason = "Degraded" + NotDegradedConditionReason = "NotDegraded" + ServingConditionReason = "Serving" + NotServingConditionReason = "NotServing" + ReadyConditionReason = "Ready" + NotReadyConditionReason = "NotReady" + ExportingConditionReason = "Exporting" + NoRulesMatchedConditionReason = "NoRulesMatched" + NoRulesDefinedConditionReason = "NoRulesDefined" + ImportingConditionReason = "Importing" + NoExportedServicesConditionReason = "NoExportedServices" +) + +// Condition describes the state of a federation at a certain point. +type Condition struct { + // Type of federation condition. + Type ConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status corev1.ConditionStatus `json:"status"` + // Last time the condition transitioned from one status to another. + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // The reason for the condition's last transition. + // +optional + Reason string `json:"reason,omitempty"` + // A human readable message indicating details about the transition. + // +optional + Message string `json:"message,omitempty"` +} + +// GetCondition removes a condition for the list of conditions +func (s *StatusConditions) GetCondition(conditionType ConditionType) Condition { + if s == nil { + return Condition{Type: conditionType, Status: corev1.ConditionUnknown} + } + for i := range s.Conditions { + if s.Conditions[i].Type == conditionType { + return s.Conditions[i] + } + } + return Condition{Type: conditionType, Status: corev1.ConditionUnknown} +} + +// SetCondition sets a specific condition in the list of conditions +func (s *StatusConditions) SetCondition(condition Condition) *StatusConditions { + if s == nil { + return nil + } + // These only get serialized out to the second. This can break update + // skipping, as the time in the resource returned from the client may not + // match the time in our cached status during a reconcile. We truncate here + // to save any problems down the line. + now := metav1.NewTime(time.Now().Truncate(time.Second)) + for i, prevCondition := range s.Conditions { + if prevCondition.Type == condition.Type { + if prevCondition.Status != condition.Status { + condition.LastTransitionTime = now + } else { + condition.LastTransitionTime = prevCondition.LastTransitionTime + } + s.Conditions[i] = condition + return s + } + } + + // If the condition does not exist, + // initialize the lastTransitionTime + condition.LastTransitionTime = now + s.Conditions = append(s.Conditions, condition) + return s +} + +// RemoveCondition removes a condition for the list of conditions +func (s *StatusConditions) RemoveCondition(conditionType ConditionType) *StatusConditions { + if s == nil { + return nil + } + for i := range s.Conditions { + if s.Conditions[i].Type == conditionType { + s.Conditions = append(s.Conditions[:i], s.Conditions[i+1:]...) + return s + } + } + return s +} diff --git a/federation/v1/zz_generated.deepcopy.go b/federation/v1/zz_generated.deepcopy.go index ede9ebfc..bb1bb523 100644 --- a/federation/v1/zz_generated.deepcopy.go +++ b/federation/v1/zz_generated.deepcopy.go @@ -22,6 +22,22 @@ import ( 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 *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiscoveryConnectionStatus) DeepCopyInto(out *DiscoveryConnectionStatus) { *out = *in @@ -182,6 +198,7 @@ func (in *ExportedServiceSetSpec) DeepCopy() *ExportedServiceSetSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExportedServiceSetStatus) DeepCopyInto(out *ExportedServiceSetStatus) { *out = *in + in.StatusConditions.DeepCopyInto(&out.StatusConditions) if in.ExportedServices != nil { in, out := &in.ExportedServices, &out.ExportedServices *out = make([]PeerServiceMapping, len(*in)) @@ -323,6 +340,7 @@ func (in *ImportedServiceSetSpec) DeepCopy() *ImportedServiceSetSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ImportedServiceSetStatus) DeepCopyInto(out *ImportedServiceSetStatus) { *out = *in + in.StatusConditions.DeepCopyInto(&out.StatusConditions) if in.ImportedServices != nil { in, out := &in.ImportedServices, &out.ImportedServices *out = make([]PeerServiceMapping, len(*in)) @@ -595,6 +613,7 @@ func (in *ServiceMeshPeerSpec) DeepCopy() *ServiceMeshPeerSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceMeshPeerStatus) DeepCopyInto(out *ServiceMeshPeerStatus) { *out = *in + in.StatusConditions.DeepCopyInto(&out.StatusConditions) in.DiscoveryStatus.DeepCopyInto(&out.DiscoveryStatus) } @@ -643,3 +662,25 @@ func (in *ServiceNameMapping) DeepCopy() *ServiceNameMapping { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StatusConditions) DeepCopyInto(out *StatusConditions) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatusConditions. +func (in *StatusConditions) DeepCopy() *StatusConditions { + if in == nil { + return nil + } + out := new(StatusConditions) + in.DeepCopyInto(out) + return out +} diff --git a/manifests/federation.maistra.io_exportedservicesets.yaml b/manifests/federation.maistra.io_exportedservicesets.yaml index 6ff179e6..03ef3d2b 100644 --- a/manifests/federation.maistra.io_exportedservicesets.yaml +++ b/manifests/federation.maistra.io_exportedservicesets.yaml @@ -116,6 +116,32 @@ spec: type: object status: properties: + conditions: + description: Represents the latest available observations of a federation's current state. + items: + description: Condition describes the state of a federation at a certain point. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of federation condition. + type: string + required: + - status + - type + type: object + type: array exportedServices: description: Exports provides details about the services exported by this mesh. items: diff --git a/manifests/federation.maistra.io_importedservicesets.yaml b/manifests/federation.maistra.io_importedservicesets.yaml index 76d65c24..4ec9e04a 100644 --- a/manifests/federation.maistra.io_importedservicesets.yaml +++ b/manifests/federation.maistra.io_importedservicesets.yaml @@ -84,6 +84,32 @@ spec: type: object status: properties: + conditions: + description: Represents the latest available observations of a federation's current state. + items: + description: Condition describes the state of a federation at a certain point. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of federation condition. + type: string + required: + - status + - type + type: object + type: array importedServices: description: Imports provides details about the services imported by this mesh. items: diff --git a/manifests/federation.maistra.io_servicemeshpeers.yaml b/manifests/federation.maistra.io_servicemeshpeers.yaml index d0d5cdc7..e8d70fd7 100644 --- a/manifests/federation.maistra.io_servicemeshpeers.yaml +++ b/manifests/federation.maistra.io_servicemeshpeers.yaml @@ -100,6 +100,32 @@ spec: status: description: ServiceMeshPeerStatus provides information related to the other mesh. properties: + conditions: + description: Represents the latest available observations of a federation's current state. + items: + description: Condition describes the state of a federation at a certain point. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of federation condition. + type: string + required: + - status + - type + type: object + type: array discoveryStatus: description: DiscoveryStatus represents the discovery status of each pilot/istiod pod in the mesh. properties: