Skip to content

Commit

Permalink
refactor(multicluster): Replace use of unstructured API with typed bi…
Browse files Browse the repository at this point in the history
…ndings for Link CR (#13420)

The linkerd-multicluster extension uses client-go's `unstructured` API to access Link custom resources.  This API allowed us to develop quickly without the work of generating typed bindings.  However, using the unstrucutred API is error prone since fields must be accessed by their string name.  It is also inconsistent with the rest of the project which uses typed bindings.

We replace the use of the unstructured API for Link resources with generated typed bindings.  The client-go APIs are slightly different and client-go does not provide a way to update subresources for typed bindings.  Therefore, when updating a Link's status subresource, we use a patch instead of an update.

Signed-off-by: Alex Leong <[email protected]>
  • Loading branch information
adleong authored Dec 10, 2024
1 parent 8987e2d commit 396af7c
Show file tree
Hide file tree
Showing 39 changed files with 1,835 additions and 1,032 deletions.
5 changes: 2 additions & 3 deletions bin/update-codegen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ git clone --depth 1 --branch "$GEN_VER" https://github.com/kubernetes/code-gener
rm -rf "${SCRIPT_ROOT}/controller/gen/client/clientset/*"
rm -rf "${SCRIPT_ROOT}/controller/gen/client/listeners/*"
rm -rf "${SCRIPT_ROOT}/controller/gen/client/informers/*"
crds=(serviceprofile:v1alpha2 server:v1beta1 serverauthorization:v1beta1 link:v1alpha1 policy:v1alpha1 policy:v1beta3 externalworkload:v1beta1)
crds=(serviceprofile server serverauthorization link policy policy externalworkload)
for crd in "${crds[@]}"
do
crd_path=$(tr : / <<< "$crd")
rm -f "${SCRIPT_ROOT}/controller/gen/apis/${crd_path}/zz_generated.deepcopy.go"
rm -f "${SCRIPT_ROOT}"/controller/gen/apis/"${crd}"/*/zz_generated.deepcopy.go
done

# shellcheck disable=SC1091
Expand Down
9 changes: 6 additions & 3 deletions controller/gen/apis/link/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@ type LinkSpec struct {
GatewayIdentity string `json:"gatewayIdentity,omitempty"`
ProbeSpec ProbeSpec `json:"probeSpec,omitempty"`
Selector metav1.LabelSelector `json:"selector,omitempty"`
RemoteDiscoverySelector metav1.LabelSelector `json:"remoteDiscoverySelector,omitempty"`
}

// ProbeSpec for gateway health probe
type ProbeSpec struct {
Path string `json:"path,omitempty"`
Port string `json:"port,omitempty"`
Period string `json:"period,omitempty"`
Path string `json:"path,omitempty"`
Port string `json:"port,omitempty"`
Period string `json:"period,omitempty"`
Timeout string `json:"timeout,omitempty"`
FailureThreshold string `json:"failureThreshold,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down
1 change: 1 addition & 0 deletions controller/gen/apis/link/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions controller/gen/apis/link/v1alpha2/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// +k8s:deepcopy-gen=package

package v1alpha2
48 changes: 48 additions & 0 deletions controller/gen/apis/link/v1alpha2/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package v1alpha2

import (
"github.com/linkerd/linkerd2/controller/gen/apis/link"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)

var (
// SchemeGroupVersion is the identifier for the API which includes the name
// of the group and the version of the API.
SchemeGroupVersion = schema.GroupVersion{
Group: link.GroupName,
Version: "v1alpha2",
}

// SchemeBuilder collects functions that add things to a scheme. It's to
// allow code to compile without explicitly referencing generated types.
// You should declare one in each package that will have generated deep
// copy or conversion functions.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)

// AddToScheme applies all the stored functions to the scheme. A non-nil error
// indicates that one function failed and the attempt was abandoned.
AddToScheme = SchemeBuilder.AddToScheme
)

// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}

// Resource takes an unqualified resource and returns a Group qualified
// GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}

// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Link{},
&LinkList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
110 changes: 110 additions & 0 deletions controller/gen/apis/link/v1alpha2/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package v1alpha2

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +groupName=multicluster.linkerd.io

type Link struct {
// TypeMeta is the metadata for the resource, like kind and apiversion
metav1.TypeMeta `json:",inline"`

// ObjectMeta contains the metadata for the particular object, including
// things like...
// - name
// - namespace
// - self link
// - labels
// - ... etc ...
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec is the custom resource spec
Spec LinkSpec `json:"spec"`

// Status defines the current state of a Link
Status LinkStatus `json:"status,omitempty"`
}

// LinkSpec specifies a LinkSpec resource.
type LinkSpec struct {
TargetClusterName string `json:"targetClusterName,omitempty"`
TargetClusterDomain string `json:"targetClusterDomain,omitempty"`
TargetClusterLinkerdNamespace string `json:"targetClusterLinkerdNamespace,omitempty"`
ClusterCredentialsSecret string `json:"clusterCredentialsSecret,omitempty"`
GatewayAddress string `json:"gatewayAddress,omitempty"`
GatewayPort string `json:"gatewayPort,omitempty"`
GatewayIdentity string `json:"gatewayIdentity,omitempty"`
ProbeSpec ProbeSpec `json:"probeSpec,omitempty"`
Selector *metav1.LabelSelector `json:"selector,omitempty"`
RemoteDiscoverySelector *metav1.LabelSelector `json:"remoteDiscoverySelector,omitempty"`
FederatedServiceSelector *metav1.LabelSelector `json:"federatedServiceSelector,omitempty"`
}

// ProbeSpec for gateway health probe
type ProbeSpec struct {
Path string `json:"path,omitempty"`
Port string `json:"port,omitempty"`
Period string `json:"period,omitempty"`
Timeout string `json:"timeout,omitempty"`
FailureThreshold string `json:"failureThreshold,omitempty"`
}

// LinkStatus holds information about the status services mirrored with this
// Link.
type LinkStatus struct {
// +optional
MirrorServices []ServiceStatus `json:"mirrorServices,omitempty"`
// +optional
FederatedServices []ServiceStatus `json:"federatedServices,omitempty"`
}

type ServiceStatus struct {
Conditions []LinkCondition `json:"conditions,omitempty"`
ControllerName string `json:"controllerName,omitempty"`
RemoteRef ObjectRef `json:"remoteRef,omitempty"`
}

// LinkCondition represents the service state of an ExternalWorkload
type LinkCondition struct {
// Type of the condition
Type string `json:"type"`
// Status of the condition.
// Can be True, False, Unknown
Status metav1.ConditionStatus `json:"status"`
// Last time an ExternalWorkload was probed for a condition.
// +optional
LastProbeTime metav1.Time `json:"lastProbeTime,omitempty"`
// Last time a condition transitioned from one status to another.
// +optional
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
// Unique one word reason in CamelCase that describes the reason for a
// transition.
// +optional
Reason string `json:"reason,omitempty"`
// Human readable message that describes details about last transition.
// +optional
Message string `json:"message"`
// LocalRef is a reference to the local mirror or federated service.
LocalRef ObjectRef `json:"localRef,omitempty"`
}

type ObjectRef struct {
Group string `json:"group,omitempty"`
Kind string `json:"kind,omitempty"`
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// LinkList is a list of LinkList resources.
type LinkList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`

Items []Link `json:"items"`
}
Loading

0 comments on commit 396af7c

Please sign in to comment.