diff --git a/frontend/cache.go b/frontend/cache.go index 85c8a3df9..21c4ccf97 100644 --- a/frontend/cache.go +++ b/frontend/cache.go @@ -6,22 +6,22 @@ package main import "github.com/Azure/ARO-HCP/internal/api" type cache struct { - cluster map[string]api.HCPOpenShiftCluster + cluster map[string]*api.HCPOpenShiftCluster } // NewCache returns a new cache. func NewCache() *cache { return &cache{ - cluster: make(map[string]api.HCPOpenShiftCluster), + cluster: make(map[string]*api.HCPOpenShiftCluster), } } -func (c *cache) GetCluster(id string) (api.HCPOpenShiftCluster, bool) { +func (c *cache) GetCluster(id string) (*api.HCPOpenShiftCluster, bool) { cluster, found := c.cluster[id] return cluster, found } -func (c *cache) SetCluster(id string, cluster api.HCPOpenShiftCluster) { +func (c *cache) SetCluster(id string, cluster *api.HCPOpenShiftCluster) { c.cluster[id] = cluster } diff --git a/frontend/frontend.go b/frontend/frontend.go index e89e73489..20cdb9868 100644 --- a/frontend/frontend.go +++ b/frontend/frontend.go @@ -180,16 +180,18 @@ func (f *Frontend) ArmResourceListByResourceGroup(writer http.ResponseWriter, re func (f *Frontend) ArmResourceRead(writer http.ResponseWriter, request *http.Request) { ctx := request.Context() logger := ctx.Value(ContextKeyLogger).(*slog.Logger) - versionedInterface, _ := ctx.Value(ContextKeyVersion).(api.Version) + versionedInterface := ctx.Value(ContextKeyVersion).(api.Version) logger.Info(fmt.Sprintf("%s: ArmResourceRead", versionedInterface)) - resourceID := strings.ToLower(request.URL.Path) + // URL path is already lowercased by middleware. + resourceID := request.URL.Path cluster, found := f.cache.GetCluster(resourceID) if !found { writer.WriteHeader(http.StatusNotFound) return } - resp, err := json.Marshal(cluster) + versionedResource := versionedInterface.NewHCPOpenShiftCluster(cluster) + resp, err := json.Marshal(versionedResource) if err != nil { f.logger.Error(err.Error()) writer.WriteHeader(http.StatusInternalServerError) @@ -205,18 +207,26 @@ func (f *Frontend) ArmResourceRead(writer http.ResponseWriter, request *http.Req func (f *Frontend) ArmResourceCreateOrUpdate(writer http.ResponseWriter, request *http.Request) { ctx := request.Context() logger := ctx.Value(ContextKeyLogger).(*slog.Logger) - versionedInterface, _ := ctx.Value(ContextKeyVersion).(api.Version) + versionedInterface := ctx.Value(ContextKeyVersion).(api.Version) logger.Info(fmt.Sprintf("%s: ArmResourceCreateOrUpdate", versionedInterface)) - resourceID := strings.ToLower(request.URL.Path) - cluster, err := clusterFromRequest(ctx.Value(ContextKeyBody).([]byte)) + + // URL path is already lowercased by middleware. + resourceID := request.URL.Path + cluster, updating := f.cache.GetCluster(resourceID) + if !updating { + cluster = api.NewDefaultHCPOpenShiftCluster() + } + body := ctx.Value(ContextKeyBody).([]byte) + err := versionedInterface.UnmarshalHCPOpenShiftCluster(body, updating, cluster) if err != nil { f.logger.Error(err.Error()) writer.WriteHeader(http.StatusBadRequest) } f.cache.SetCluster(resourceID, cluster) - resp, err := json.Marshal(cluster) + versionedResource := versionedInterface.NewHCPOpenShiftCluster(cluster) + resp, err := json.Marshal(versionedResource) if err != nil { f.logger.Error(err.Error()) writer.WriteHeader(http.StatusInternalServerError) @@ -242,10 +252,11 @@ func (f *Frontend) ArmResourcePatch(writer http.ResponseWriter, request *http.Re func (f *Frontend) ArmResourceDelete(writer http.ResponseWriter, request *http.Request) { ctx := request.Context() logger := ctx.Value(ContextKeyLogger).(*slog.Logger) - versionedInterface, _ := ctx.Value(ContextKeyVersion).(api.Version) + versionedInterface := ctx.Value(ContextKeyVersion).(api.Version) logger.Info(fmt.Sprintf("%s: ArmResourceDelete", versionedInterface)) - resourceID := strings.ToLower(request.URL.Path) + // URL path is already lowercased by middleware. + resourceID := request.URL.Path _, found := f.cache.GetCluster(resourceID) if !found { writer.WriteHeader(http.StatusNotFound) @@ -265,12 +276,3 @@ func (f *Frontend) ArmResourceAction(writer http.ResponseWriter, request *http.R writer.WriteHeader(http.StatusOK) } - -func clusterFromRequest(body []byte) (api.HCPOpenShiftCluster, error) { - var cluster api.HCPOpenShiftCluster - err := json.Unmarshal(body, &cluster) - if err != nil { - return api.HCPOpenShiftCluster{}, err - } - return cluster, nil -} diff --git a/frontend/middleware_validateapi.go b/frontend/middleware_validateapi.go index 452ccdb6a..74af6aecc 100644 --- a/frontend/middleware_validateapi.go +++ b/frontend/middleware_validateapi.go @@ -6,7 +6,6 @@ package main import ( "context" "net/http" - "strings" "github.com/Azure/ARO-HCP/internal/api" "github.com/Azure/ARO-HCP/internal/api/arm" @@ -20,9 +19,6 @@ func MiddlewareValidateAPIVersion(w http.ResponseWriter, r *http.Request, next h arm.CloudErrorCodeInvalidParameter, "", "The request is missing required parameter '%s'.", APIVersionKey) - } else if strings.EqualFold(apiVersion, "cache") { - r = r.WithContext(context.WithValue(r.Context(), ContextKeyVersion, "cache")) - next(w, r) } else if version, ok := api.Lookup(apiVersion); !ok { arm.WriteError( w, http.StatusBadRequest, diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 000000000..2a3453643 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,19 @@ +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= diff --git a/internal/api/enums.go b/internal/api/enums.go index caaeaa27f..2cbf6757f 100644 --- a/internal/api/enums.go +++ b/internal/api/enums.go @@ -5,6 +5,46 @@ package api import "fmt" +// NetworkType represents an OpenShift cluster network plugin. +type NetworkType int + +const ( + NetworkTypeOpenShiftSDN NetworkType = iota + NetworkTypeOVNKubernetes + + NetworkTypeOther // catch-all, must be last +) + +func (v NetworkType) String() string { + switch v { + case NetworkTypeOpenShiftSDN: + return "OpenShiftSDN" + case NetworkTypeOVNKubernetes: + return "OVNKubernetes" + default: + return "Other" + } +} + +func (v NetworkType) MarshalText() (text []byte, err error) { + // NetworkTypeOther is a catch-all value. + text = []byte(v.String()) + return +} + +func (v *NetworkType) UnmarshalText(text []byte) error { + for i := range NetworkTypeOther { + if i.String() == string(text) { + *v = i + return nil + } + } + + // NetworkTypeOther is a catch-all value. + *v = NetworkTypeOther + return nil +} + // OutboundType represents a routing strategy to provide egress to the Internet. type OutboundType int diff --git a/internal/api/enums_test.go b/internal/api/enums_test.go index 505305290..5ed5966c6 100644 --- a/internal/api/enums_test.go +++ b/internal/api/enums_test.go @@ -10,6 +10,71 @@ import ( "testing" ) +func TestNetworkType(t *testing.T) { + // Ensure NetworkType implementes these interfaces + var i NetworkType + _ = fmt.Stringer(i) + _ = encoding.TextMarshaler(i) + _ = encoding.TextUnmarshaler(&i) + + for _, tt := range []struct { + name string + val int + str string + skipMarshal bool + skipUnmarshal bool + }{ + { + name: "NetworkTypeOpenShiftSDN", + val: int(NetworkTypeOpenShiftSDN), + str: fmt.Sprintf("%q", NetworkTypeOpenShiftSDN), + }, + { + name: "NetworkTypeOVNKubernetes", + val: int(NetworkTypeOVNKubernetes), + str: fmt.Sprintf("%q", NetworkTypeOVNKubernetes), + }, + { + name: "NetworkTypeOther", + val: int(NetworkTypeOther), + str: fmt.Sprintf("%q", NetworkTypeOther), + }, + { + name: "Unknown NetworkType string", + val: int(NetworkTypeOther), + str: "\"unknown\"", + skipMarshal: true, + }, + { + name: "Unknown NetworkType value", + val: -1, + str: fmt.Sprintf("%q", NetworkTypeOther), + skipUnmarshal: true, + }, + } { + if !tt.skipMarshal { + t.Logf("Marshaling %d", tt.val) + data, err := json.Marshal(NetworkType(tt.val)) + if err != nil { + t.Fatalf("Marshal: Unexpected error: %s", err) + } else if string(data) != tt.str { + t.Fatalf("Marshal: Expected %s, got %s", tt.str, string(data)) + } + } + + if !tt.skipUnmarshal { + var val NetworkType + t.Logf("Unmarshaling %s", tt.str) + err := json.Unmarshal([]byte(tt.str), &val) + if err != nil { + t.Fatalf("Unmarshal: Unexpected error: %s", err) + } else if int(val) != tt.val { + t.Fatalf("Unmarshal: Expected %d, got %d", tt.val, val) + } + } + } +} + func TestOutboundType(t *testing.T) { // Ensure OutboundType implements these interfaces var i OutboundType diff --git a/internal/api/hcpopenshiftcluster.go b/internal/api/hcpopenshiftcluster.go index ec268c598..a5f2a6992 100644 --- a/internal/api/hcpopenshiftcluster.go +++ b/internal/api/hcpopenshiftcluster.go @@ -5,6 +5,9 @@ package api import ( "net" + "net/url" + + configv1 "github.com/openshift/api/config/v1" "github.com/Azure/ARO-HCP/internal/api/arm" "github.com/Azure/ARO-HCP/internal/api/json" @@ -19,106 +22,103 @@ type HCPOpenShiftCluster struct { // HCPOpenShiftClusterProperties represents the property bag of a HCPOpenShiftCluster resource. type HCPOpenShiftClusterProperties struct { ProvisioningState arm.ProvisioningState `json:"provisioningState,omitempty" visibility:"read"` - ClusterProfile ClusterProfile `json:"clusterProfile,omitempty" visibility:"read,create,update"` - ProxyProfile ProxyProfile `json:"proxyProfile,omitempty" visibility:"read,create,update"` - APIProfile APIProfile `json:"apiProfile,omitempty" visibility:"read,create"` - ConsoleProfile ConsoleProfile `json:"consoleProfile,omitempty" visibility:"read,create,update"` - IngressProfile IngressProfile `json:"ingressProfile,omitempty" visibility:"read,create"` - NetworkProfile NetworkProfile `json:"networkProfile,omitempty" visibility:"read,create"` - NodePoolProfiles []*NodePoolProfile `json:"nodePoolProfiles,omitempty" visibility:"read"` - EtcdEncryption EtcdEncryptionProfile `json:"etcdEncryption,omitempty" visibility:"read,create"` + Spec ClusterSpec `json:"spec,omitempty" visibility:"read,create,update"` } -// ClusterProfile represents a high level cluster configuration. -type ClusterProfile struct { - ControlPlaneVersion string `json:"controlPlaneVersion,omitempty" visibility:"read,create,update"` - SubnetID string `json:"subnetId,omitempty" visibility:"read,create"` - ManagedResourceGroup string `json:"managedResourceGroup,omitempty" visibility:"read,create"` - OIDCIssuerURL json.URL `json:"oidcIssuerUrl,omitempty" visibility:"read"` +// ClusterSpec represents a high level cluster configuration. +type ClusterSpec struct { + Version VersionProfile `json:"version,omitempty" visibility:"read,create,update"` + DNS DNSProfile `json:"dns,omitempty" visibility:"read,create,update"` + Network NetworkProfile `json:"network,omitempty" visibility:"read,create"` + Console ConsoleProfile `json:"console,omitempty" visibility:"read"` + API APIProfile `json:"api,omitempty" visibility:"read,create"` + FIPS bool `json:"fips,omitempty" visibility:"read,create"` + EtcdEncryption bool `json:"etcdEncryption,omitempty" visibility:"read,create"` + DisableUserWorkloadMonitoring bool `json:"disableUserWorkloadMonitoring,omitempty" visibility:"read,create,update"` + Proxy ProxyProfile `json:"proxy,omitempty" visibility:"read,create,update"` + Platform PlatformProfile `json:"platform,omitempty" visibility:"read,create"` + IssuerURL url.URL `json:"issuerUrl,omitempty" visibility:"read"` + ExternalAuth ExternalAuthConfigProfile `json:"externalAuth,omitempty" visibility:"read,create"` + Ingress []*IngressProfile `json:"ingressProfile,omitempty" visibility:"read,create"` } -// ProxyProfile represents the cluster proxy configuration. -// Visibility for the entire struct is "read,create,update". -type ProxyProfile struct { - HTTPProxy string `json:"httpProxy,omitempty"` - HTTPSProxy string `json:"httpsProxy,omitempty"` - NoProxy string `json:"noProxy,omitempty"` - TrustedCA string `json:"trustedCa,omitempty"` +// VersionProfile represents the cluster control plane version. +type VersionProfile struct { + ID string `json:"id,omitempty" visibility:"read,create,update"` + ChannelGroup string `json:"channelGroup,omitempty" visibility:"read,create"` + AvailableUpgrades []string `json:"availableUpgrades,omitempty" visibility:"read"` } -// APIProfile represents a cluster API server configuration. +// DNSProfile represents the DNS configuration of the cluster. +type DNSProfile struct { + BaseDomain string `json:"baseDomain,omitempty" visibility:"read"` + BaseDomainPrefix string `json:"baseDomainPrefix,omitempty" visibility:"read,create"` +} + +// NetworkProfile represents a cluster network configuration. // Visibility for the entire struct is "read,create". -type APIProfile struct { - URL json.URL `json:"url,omitempty"` - IP net.IP `json:"ip,omitempty"` - Visibility Visibility `json:"visibility,omitempty"` +type NetworkProfile struct { + NetworkType NetworkType `json:"networkType,omitempty"` + PodCIDR json.IPNet `json:"podCidr,omitempty"` + ServiceCIDR json.IPNet `json:"serviceCidr,omitempty"` + MachineCIDR json.IPNet `json:"machineCidr,omitempty"` + HostPrefix int32 `json:"hostPrefix,omitempty"` } // ConsoleProfile represents a cluster web console configuration. +// Visibility for the entire struct is "read". type ConsoleProfile struct { - URL json.URL `json:"url,omitempty" visibility:"read"` - FIPS bool `json:"fips,omitempty" visibility:"read,create,update"` + URL url.URL `json:"url,omitempty"` } -// IngressProfile represents a cluster ingress configuration. -type IngressProfile struct { +// APIProfile represents a cluster API server configuration. +type APIProfile struct { + URL url.URL `json:"url,omitempty" visibility:"read"` IP net.IP `json:"ip,omitempty" visibility:"read"` - URL json.URL `json:"url,omitempty" visibility:"read"` Visibility Visibility `json:"visibility,omitempty" visibility:"read,create"` } -// NetworkProfile represents a cluster network configuration. -// Visibility for the entire struct is "read,create". -type NetworkProfile struct { - PodCIDR json.IPNet `json:"podCidr,omitempty"` - ServiceCIDR json.IPNet `json:"serviceCidr,omitempty"` - MachineCIDR json.IPNet `json:"machineCidr,omitempty"` - HostPrefix int32 `json:"hostPrefix,omitempty"` - OutboundType OutboundType `json:"outboundType,omitempty"` - PreconfiguredNSGs bool `json:"preconfiguredNsgs,omitempty"` +// ProxyProfile represents the cluster proxy configuration. +// Visibility for the entire struct is "read,create,update". +type ProxyProfile struct { + HTTPProxy string `json:"httpProxy,omitempty"` + HTTPSProxy string `json:"httpsProxy,omitempty"` + NoProxy string `json:"noProxy,omitempty"` + TrustedCA string `json:"trustedCa,omitempty"` } -// NodePoolAutoscaling represents a node pool autoscaling configuration. -// Visibility for the entire struct is "read". -type NodePoolAutoscaling struct { - MinReplicas int32 `json:"minReplicas,omitempty"` - MaxReplicas int32 `json:"maxReplicas,omitempty"` +// PlatformProfile represents the Azure platform configuration. +// Visibility for the entire struct is "read,create". +type PlatformProfile struct { + ManagedResourceGroup string `json:"managedResourceGroup,omitempty"` + SubnetID string `json:"subnetId,omitempty"` + OutboundType OutboundType `json:"outboundType,omitempty"` + PreconfiguredNSGs bool `json:"preconfiguredNsgs,omitempty"` + EtcdEncryptionSetID string `json:"etcdEncryptionSetId,omitempty"` } -// NodePoolProfile represents a worker node pool configuration. -// Visibility for the entire struct is "read". -type NodePoolProfile struct { - Name string `json:"name,omitempty"` - Version string `json:"version,omitempty"` - Labels []string `json:"labels,omitempty"` - Taints []string `json:"taints,omitempty"` - DiskSize int32 `json:"diskSize,omitempty"` - EphemeralOSDisk bool `json:"ephemeralOsDisk,omitempty"` - Replicas int32 `json:"replicas,omitempty"` - SubnetID string `json:"subnetId,omitempty"` - EncryptionAtHost bool `json:"encryptionAtHost,omitempty"` - AutoRepair bool `json:"autoRepair,omitempty"` - DiscEncryptionSetID string `json:"discEncryptionSetId,omitempty"` - TuningConfigs []string `json:"tuningConfigs,omitempty"` - AvailabilityZone string `json:"availabilityZone,omitempty"` - DiscStorageAccountType string `json:"discStorageAccountType,omitempty"` - VMSize string `json:"vmSize,omitempty"` - Autoscaling NodePoolAutoscaling `json:"autoscaling,omitempty"` +// ExternalAuthConfigProfile represents the external authentication configuration. +type ExternalAuthConfigProfile struct { + Enabled bool `json:"enabled,omitempty" visibility:"read,create"` + ExternalAuths []*configv1.OIDCProvider `json:"externalAuths,omitempty" visibility:"read"` } -// EtcdEncryptionProfile represents the configuration needed for customer -// provided keys to encrypt etcd storage. -// Visibility for the entire struct is "read,create". -type EtcdEncryptionProfile struct { - DiscEncryptionSetID string `json:"discEncryptionSetId,omitempty"` +// IngressProfile represents a cluster ingress configuration. +type IngressProfile struct { + IP net.IP `json:"ip,omitempty" visibility:"read"` + URL url.URL `json:"url,omitempty" visibility:"read"` + Visibility Visibility `json:"visibility,omitempty" visibility:"read,create"` } // Creates an HCPOpenShiftCluster with any non-zero default values. func NewDefaultHCPOpenShiftCluster() *HCPOpenShiftCluster { return &HCPOpenShiftCluster{ Properties: HCPOpenShiftClusterProperties{ - NetworkProfile: NetworkProfile{ - HostPrefix: 23, + Spec: ClusterSpec{ + Network: NetworkProfile{ + NetworkType: NetworkTypeOVNKubernetes, + HostPrefix: 23, + }, }, }, } diff --git a/internal/api/hcpopenshiftclusternodepool.go b/internal/api/hcpopenshiftclusternodepool.go index b502b34ed..1c049092b 100644 --- a/internal/api/hcpopenshiftclusternodepool.go +++ b/internal/api/hcpopenshiftclusternodepool.go @@ -20,3 +20,31 @@ type HCPOpenShiftClusterNodePoolProperties struct { ProvisioningState arm.ProvisioningState `json:"provisioningState,omitempty" visibility:"read"` Profile NodePoolProfile `json:"profile,omitempty" visibility:"read,create,update"` } + +// NodePoolProfile represents a worker node pool configuration. +// Visibility for the entire struct is "read". +type NodePoolProfile struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + Labels []string `json:"labels,omitempty"` + Taints []string `json:"taints,omitempty"` + DiskSize int32 `json:"diskSize,omitempty"` + EphemeralOSDisk bool `json:"ephemeralOsDisk,omitempty"` + Replicas int32 `json:"replicas,omitempty"` + SubnetID string `json:"subnetId,omitempty"` + EncryptionAtHost bool `json:"encryptionAtHost,omitempty"` + AutoRepair bool `json:"autoRepair,omitempty"` + DiscEncryptionSetID string `json:"discEncryptionSetId,omitempty"` + TuningConfigs []string `json:"tuningConfigs,omitempty"` + AvailabilityZone string `json:"availabilityZone,omitempty"` + DiscStorageAccountType string `json:"discStorageAccountType,omitempty"` + VMSize string `json:"vmSize,omitempty"` + Autoscaling NodePoolAutoscaling `json:"autoscaling,omitempty"` +} + +// NodePoolAutoscaling represents a node pool autoscaling configuration. +// Visibility for the entire struct is "read". +type NodePoolAutoscaling struct { + MinReplicas int32 `json:"minReplicas,omitempty"` + MaxReplicas int32 `json:"maxReplicas,omitempty"` +} diff --git a/internal/api/registry.go b/internal/api/registry.go index 30b894980..6f193608a 100644 --- a/internal/api/registry.go +++ b/internal/api/registry.go @@ -15,17 +15,12 @@ const ( ) type VersionedHCPOpenShiftCluster interface { - Normalize(*HCPOpenShiftCluster) + Normalize(*HCPOpenShiftCluster) error ValidateStatic() error } type VersionedHCPOpenShiftClusterNodePool interface { - Normalize(*HCPOpenShiftClusterNodePool) - ValidateStatic() error -} - -type VersionedNodePoolProfile interface { - Normalize(*NodePoolProfile) + Normalize(*HCPOpenShiftClusterNodePool) error ValidateStatic() error } @@ -34,10 +29,10 @@ type Version interface { // Resource Types NewHCPOpenShiftCluster(*HCPOpenShiftCluster) VersionedHCPOpenShiftCluster - NewHCPOpenShiftClusterNodePool(*HCPOpenShiftClusterNodePool) VersionedHCPOpenShiftClusterNodePool + // FIXME Disable until we have generated structs for node pools. + //NewHCPOpenShiftClusterNodePool(*HCPOpenShiftClusterNodePool) VersionedHCPOpenShiftClusterNodePool - // Component Types - NewNodePoolProfile(*NodePoolProfile) VersionedNodePoolProfile + UnmarshalHCPOpenShiftCluster([]byte, bool, *HCPOpenShiftCluster) error } // apiRegistry is the map of registered API versions diff --git a/internal/api/utils.go b/internal/api/utils.go new file mode 100644 index 000000000..0c5faac3d --- /dev/null +++ b/internal/api/utils.go @@ -0,0 +1,35 @@ +package api + +import "slices" + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +// Ptr returns a pointer to p. +func Ptr[T any](p T) *T { + return &p +} + +// DeleteNilsFromPtrSlice returns a slice with nil pointers removed. +func DeleteNilsFromPtrSlice[S ~[]*E, E any](s S) S { + return slices.DeleteFunc(s, func(e *E) bool { return e == nil }) +} + +// StringSliceToStringPtrSlice converts a slice of strings to a slice of string pointers. +func StringSliceToStringPtrSlice(s []string) []*string { + out := make([]*string, len(s)) + for index, item := range s { + out[index] = Ptr(item) + } + return out +} + +// StringPtrSliceToStringSlice converts a slice of string pointers to a slice of strings. +func StringPtrSliceToStringSlice(s []*string) []string { + s = DeleteNilsFromPtrSlice(s) + out := make([]string, 0, len(s)) + for _, item := range s { + out = append(out, *item) + } + return out +} diff --git a/internal/api/v20240610preview/hcpopenshiftclusters_methods.go b/internal/api/v20240610preview/hcpopenshiftclusters_methods.go new file mode 100644 index 000000000..0a5cb6048 --- /dev/null +++ b/internal/api/v20240610preview/hcpopenshiftclusters_methods.go @@ -0,0 +1,610 @@ +package v20240610preview + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "encoding/json" + + configv1 "github.com/openshift/api/config/v1" + + "github.com/Azure/ARO-HCP/internal/api" + "github.com/Azure/ARO-HCP/internal/api/arm" +) + +func newVersionProfile(from *api.VersionProfile) *VersionProfile { + return &VersionProfile{ + ID: api.Ptr(from.ID), + ChannelGroup: api.Ptr(from.ChannelGroup), + AvailableUpgrades: api.StringSliceToStringPtrSlice(from.AvailableUpgrades), + } +} + +func newDNSProfile(from *api.DNSProfile) *DNSProfile { + return &DNSProfile{ + BaseDomain: api.Ptr(from.BaseDomain), + BaseDomainPrefix: api.Ptr(from.BaseDomainPrefix), + } +} + +func newNetworkProfile(from *api.NetworkProfile) *NetworkProfile { + return &NetworkProfile{ + NetworkType: api.Ptr(NetworkType(from.NetworkType.String())), + PodCidr: api.Ptr(from.PodCIDR.String()), + ServiceCidr: api.Ptr(from.ServiceCIDR.String()), + MachineCidr: api.Ptr(from.MachineCIDR.String()), + HostPrefix: api.Ptr(from.HostPrefix), + } +} + +func newConsoleProfile(from *api.ConsoleProfile) *ConsoleProfile { + return &ConsoleProfile{ + URL: api.Ptr(from.URL.String()), + } +} + +func newAPIProfile(from *api.APIProfile) *APIProfile { + return &APIProfile{ + URL: api.Ptr(from.URL.String()), + IP: api.Ptr(from.IP.String()), + Visibility: api.Ptr(Visibility(from.Visibility.String())), + } +} + +func newProxyProfile(from *api.ProxyProfile) *ProxyProfile { + return &ProxyProfile{ + HTTPProxy: api.Ptr(from.HTTPProxy), + HTTPSProxy: api.Ptr(from.HTTPSProxy), + NoProxy: api.Ptr(from.NoProxy), + TrustedCa: api.Ptr(from.TrustedCA), + } +} + +func newPlatformProfile(from *api.PlatformProfile) *PlatformProfile { + return &PlatformProfile{ + ManagedResourceGroup: api.Ptr(from.ManagedResourceGroup), + SubnetID: api.Ptr(from.SubnetID), + OutboundType: api.Ptr(OutboundType(from.OutboundType.String())), + PreconfiguredNsgs: api.Ptr(from.PreconfiguredNSGs), + EtcdEncryptionSetID: api.Ptr(from.EtcdEncryptionSetID), + } +} + +func newIngressProfile(from *api.IngressProfile) *IngressProfile { + return &IngressProfile{ + IP: api.Ptr(from.IP.String()), + URL: api.Ptr(from.URL.String()), + Visibility: api.Ptr(Visibility(from.Visibility.String())), + } +} + +func newExternalAuthProfile(from *configv1.OIDCProvider) *ExternalAuthProfile { + out := &ExternalAuthProfile{ + Issuer: &TokenIssuerProfile{ + URL: api.Ptr(from.Issuer.URL), + Audiences: make([]*string, len(from.Issuer.Audiences)), + Ca: api.Ptr(from.Issuer.CertificateAuthority.Name), + }, + Clients: make([]*ExternalAuthClientProfile, len(from.OIDCClients)), + Claim: &ExternalAuthClaimProfile{ + Mappings: &TokenClaimMappingsProfile{ + Username: &ClaimProfile{ + Claim: api.Ptr(from.ClaimMappings.Username.Claim), + PrefixPolicy: api.Ptr(string(from.ClaimMappings.Username.PrefixPolicy)), + }, + Groups: &ClaimProfile{ + Claim: api.Ptr(from.ClaimMappings.Groups.Claim), + Prefix: api.Ptr(from.ClaimMappings.Groups.Prefix), + }, + }, + ValidationRules: make([]*TokenClaimValidationRuleProfile, len(from.ClaimValidationRules)), + }, + } + + for index, item := range from.Issuer.Audiences { + out.Issuer.Audiences[index] = api.Ptr(string(item)) + } + + for index, item := range from.OIDCClients { + out.Clients[index] = newExternalAuthClientProfile(item) + } + + if from.ClaimMappings.Username.Prefix != nil { + out.Claim.Mappings.Username.Prefix = api.Ptr(from.ClaimMappings.Username.Prefix.PrefixString) + } + + for index, item := range from.ClaimValidationRules { + out.Claim.ValidationRules[index] = newTokenClaimValidationRuleProfile(item) + } + + return out +} + +func newTokenClaimValidationRuleProfile(from configv1.TokenClaimValidationRule) *TokenClaimValidationRuleProfile { + if from.RequiredClaim == nil { + // Should never happen since we create these rules. + panic("TokenClaimValidationRule has no RequiredClaim") + } + + return &TokenClaimValidationRuleProfile{ + Claim: api.Ptr(from.RequiredClaim.Claim), + RequiredValue: api.Ptr(from.RequiredClaim.RequiredValue), + } +} + +func newExternalAuthClientProfile(from configv1.OIDCClientConfig) *ExternalAuthClientProfile { + return &ExternalAuthClientProfile{ + Component: &ExternalAuthClientComponentProfile{ + Name: api.Ptr(from.ComponentName), + AuthClientNamespace: api.Ptr(from.ComponentNamespace), + }, + ID: api.Ptr(from.ClientID), + Secret: api.Ptr(from.ClientSecret.Name), + ExtraScopes: api.StringSliceToStringPtrSlice(from.ExtraScopes), + } +} + +func (v version) NewHCPOpenShiftCluster(from *api.HCPOpenShiftCluster) api.VersionedHCPOpenShiftCluster { + out := &HcpOpenShiftClusterResource{ + ID: api.Ptr(from.Resource.ID), + Name: api.Ptr(from.Resource.Name), + Type: api.Ptr(from.Resource.Type), + Location: api.Ptr(from.TrackedResource.Location), + Tags: map[string]*string{}, + // FIXME Skipping ManagedServiceIdentity + Properties: &HcpOpenShiftClusterProperties{ + ProvisioningState: api.Ptr(ProvisioningState(from.Properties.ProvisioningState.String())), + Spec: &ClusterSpec{ + Version: newVersionProfile(&from.Properties.Spec.Version), + DNS: newDNSProfile(&from.Properties.Spec.DNS), + Network: newNetworkProfile(&from.Properties.Spec.Network), + Console: newConsoleProfile(&from.Properties.Spec.Console), + API: newAPIProfile(&from.Properties.Spec.API), + Fips: api.Ptr(from.Properties.Spec.FIPS), + EtcdEncryption: api.Ptr(from.Properties.Spec.EtcdEncryption), + DisableUserWorkloadMonitoring: api.Ptr(from.Properties.Spec.DisableUserWorkloadMonitoring), + Proxy: newProxyProfile(&from.Properties.Spec.Proxy), + Platform: newPlatformProfile(&from.Properties.Spec.Platform), + IssuerURL: api.Ptr(from.Properties.Spec.IssuerURL.String()), + ExternalAuth: &ExternalAuthConfigProfile{ + Enabled: api.Ptr(from.Properties.Spec.ExternalAuth.Enabled), + ExternalAuths: make([]*ExternalAuthProfile, len(from.Properties.Spec.ExternalAuth.ExternalAuths)), + }, + Ingress: make([]*IngressProfile, len(from.Properties.Spec.Ingress)), + }, + }, + } + + if from.Resource.SystemData != nil { + out.SystemData = &SystemData{ + CreatedBy: api.Ptr(from.Resource.SystemData.CreatedBy), + CreatedByType: api.Ptr(CreatedByType(from.Resource.SystemData.CreatedByType.String())), + CreatedAt: from.Resource.SystemData.CreatedAt, + LastModifiedBy: api.Ptr(from.Resource.SystemData.LastModifiedBy), + LastModifiedByType: api.Ptr(CreatedByType(from.Resource.SystemData.LastModifiedByType.String())), + LastModifiedAt: from.Resource.SystemData.LastModifiedAt, + } + } + + for key, val := range from.TrackedResource.Tags { + out.Tags[key] = api.Ptr(val) + } + + for index, item := range from.Properties.Spec.ExternalAuth.ExternalAuths { + out.Properties.Spec.ExternalAuth.ExternalAuths[index] = newExternalAuthProfile(item) + } + + for index, item := range from.Properties.Spec.Ingress { + out.Properties.Spec.Ingress[index] = newIngressProfile(item) + } + + return out +} + +func (v version) UnmarshalHCPOpenShiftCluster(data []byte, updating bool, out *api.HCPOpenShiftCluster) error { + var resource HcpOpenShiftClusterResource + + err := json.Unmarshal(data, &resource) + if err != nil { + return err + } + + // FIXME Pass updating flag and possibly other flags. + return resource.Normalize(out) +} + +func (c *HcpOpenShiftClusterResource) Normalize(out *api.HCPOpenShiftCluster) error { + if c.ID != nil { + out.Resource.ID = *c.ID + } + if c.Name != nil { + out.Resource.Name = *c.Name + } + if c.Type != nil { + out.Resource.Type = *c.Type + } + if c.SystemData != nil { + out.Resource.SystemData = &arm.SystemData{ + CreatedAt: c.SystemData.CreatedAt, + LastModifiedAt: c.SystemData.LastModifiedAt, + } + if c.SystemData.CreatedBy != nil { + out.Resource.SystemData.CreatedBy = *c.SystemData.CreatedBy + } + if c.SystemData.CreatedByType != nil { + text := []byte(*c.SystemData.CreatedByType) + err := out.Resource.SystemData.CreatedByType.UnmarshalText(text) + if err != nil { + return err + } + } + if c.SystemData.LastModifiedBy != nil { + out.Resource.SystemData.LastModifiedBy = *c.SystemData.LastModifiedBy + } + if c.SystemData.LastModifiedByType != nil { + text := []byte(*c.SystemData.LastModifiedByType) + err := out.Resource.SystemData.LastModifiedByType.UnmarshalText(text) + if err != nil { + return err + } + } + } + // FIXME Skipping ManagedServiceIdentity + if c.Location != nil { + out.TrackedResource.Location = *c.Location + } + out.Tags = make(map[string]string) + for k, v := range c.Tags { + if v != nil { + out.Tags[k] = *v + } + } + if c.Properties != nil { + if c.Properties.ProvisioningState != nil { + text := []byte(*c.Properties.ProvisioningState) + err := out.Properties.ProvisioningState.UnmarshalText(text) + if err != nil { + return err + } + } + if c.Properties.Spec != nil { + if c.Properties.Spec.Version != nil { + err := c.Properties.Spec.Version.Normalize(&out.Properties.Spec.Version) + if err != nil { + return err + } + } + if c.Properties.Spec.DNS != nil { + err := c.Properties.Spec.DNS.Normalize(&out.Properties.Spec.DNS) + if err != nil { + return err + } + } + if c.Properties.Spec.Network != nil { + err := c.Properties.Spec.Network.Normalize(&out.Properties.Spec.Network) + if err != nil { + return err + } + } + if c.Properties.Spec.Console != nil { + err := c.Properties.Spec.Console.Normalize(&out.Properties.Spec.Console) + if err != nil { + return err + } + } + if c.Properties.Spec.API != nil { + err := c.Properties.Spec.API.Normalize(&out.Properties.Spec.API) + if err != nil { + return err + } + } + if c.Properties.Spec.Fips != nil { + out.Properties.Spec.FIPS = *c.Properties.Spec.Fips + } + if c.Properties.Spec.EtcdEncryption != nil { + out.Properties.Spec.EtcdEncryption = *c.Properties.Spec.EtcdEncryption + } + if c.Properties.Spec.DisableUserWorkloadMonitoring != nil { + out.Properties.Spec.DisableUserWorkloadMonitoring = *c.Properties.Spec.DisableUserWorkloadMonitoring + } + if c.Properties.Spec.Proxy != nil { + err := c.Properties.Spec.Proxy.Normalize(&out.Properties.Spec.Proxy) + if err != nil { + return err + } + } + if c.Properties.Spec.Platform != nil { + err := c.Properties.Spec.Platform.Normalize(&out.Properties.Spec.Platform) + if err != nil { + return err + } + } + if c.Properties.Spec.IssuerURL != nil { + text := []byte(*c.Properties.Spec.IssuerURL) + err := out.Properties.Spec.IssuerURL.UnmarshalBinary(text) + if err != nil { + return err + } + } + if c.Properties.Spec.ExternalAuth != nil { + err := c.Properties.Spec.ExternalAuth.Normalize(&out.Properties.Spec.ExternalAuth) + if err != nil { + return err + } + } + ingressSequence := api.DeleteNilsFromPtrSlice(c.Properties.Spec.Ingress) + out.Properties.Spec.Ingress = make([]*api.IngressProfile, len(ingressSequence)) + for index, item := range ingressSequence { + out.Properties.Spec.Ingress[index] = &api.IngressProfile{} + err := item.Normalize(out.Properties.Spec.Ingress[index]) + if err != nil { + return err + } + } + } + } + + return nil +} + +func (c *HcpOpenShiftClusterResource) ValidateStatic() error { + return nil +} + +func (p *VersionProfile) Normalize(out *api.VersionProfile) error { + if p.ID != nil { + out.ID = *p.ID + } + if p.ChannelGroup != nil { + out.ChannelGroup = *p.ChannelGroup + } + out.AvailableUpgrades = api.StringPtrSliceToStringSlice(p.AvailableUpgrades) + + return nil +} + +func (p *DNSProfile) Normalize(out *api.DNSProfile) error { + if p.BaseDomain != nil { + out.BaseDomain = *p.BaseDomain + } + if p.BaseDomainPrefix != nil { + out.BaseDomainPrefix = *p.BaseDomainPrefix + } + + return nil +} + +func (p *NetworkProfile) Normalize(out *api.NetworkProfile) error { + if p.NetworkType != nil { + text := []byte(*p.NetworkType) + err := out.NetworkType.UnmarshalText(text) + if err != nil { + return err + } + } + if p.PodCidr != nil { + text := []byte(*p.PodCidr) + err := out.PodCIDR.UnmarshalText(text) + if err != nil { + return err + } + } + if p.ServiceCidr != nil { + text := []byte(*p.ServiceCidr) + err := out.ServiceCIDR.UnmarshalText(text) + if err != nil { + return err + } + } + if p.MachineCidr != nil { + text := []byte(*p.MachineCidr) + err := out.MachineCIDR.UnmarshalText(text) + if err != nil { + return err + } + } + if p.HostPrefix != nil { + out.HostPrefix = *p.HostPrefix + } + + return nil +} + +func (p *ConsoleProfile) Normalize(out *api.ConsoleProfile) error { + if p.URL != nil { + text := []byte(*p.URL) + err := out.URL.UnmarshalBinary(text) + if err != nil { + return err + } + } + + return nil +} + +func (p *APIProfile) Normalize(out *api.APIProfile) error { + if p.URL != nil { + text := []byte(*p.URL) + err := out.URL.UnmarshalBinary(text) + if err != nil { + return err + } + } + if p.IP != nil { + text := []byte(*p.IP) + err := out.IP.UnmarshalText(text) + if err != nil { + return err + } + } + if p.Visibility != nil { + text := []byte(*p.Visibility) + err := out.Visibility.UnmarshalText(text) + if err != nil { + return err + } + } + + return nil +} + +func (p *ProxyProfile) Normalize(out *api.ProxyProfile) error { + if p.HTTPProxy != nil { + out.HTTPProxy = *p.HTTPProxy + } + if p.HTTPSProxy != nil { + out.HTTPSProxy = *p.HTTPSProxy + } + if p.NoProxy != nil { + out.NoProxy = *p.NoProxy + } + if p.TrustedCa != nil { + out.TrustedCA = *p.TrustedCa + } + + return nil +} + +func (p *PlatformProfile) Normalize(out *api.PlatformProfile) error { + if p.ManagedResourceGroup != nil { + out.ManagedResourceGroup = *p.ManagedResourceGroup + } + if p.SubnetID != nil { + out.SubnetID = *p.SubnetID + } + if p.OutboundType != nil { + text := []byte(*p.OutboundType) + err := out.OutboundType.UnmarshalText(text) + if err != nil { + return err + } + } + if p.PreconfiguredNsgs != nil { + out.PreconfiguredNSGs = *p.PreconfiguredNsgs + } + if p.EtcdEncryptionSetID != nil { + out.EtcdEncryptionSetID = *p.EtcdEncryptionSetID + } + + return nil +} + +func (p *ExternalAuthConfigProfile) Normalize(out *api.ExternalAuthConfigProfile) error { + if p.Enabled != nil { + out.Enabled = *p.Enabled + } + out.ExternalAuths = []*configv1.OIDCProvider{} + for _, item := range api.DeleteNilsFromPtrSlice(p.ExternalAuths) { + provider := &configv1.OIDCProvider{} + + if item.Issuer != nil { + if item.Issuer.URL != nil { + provider.Issuer.URL = *item.Issuer.URL + } + provider.Issuer.Audiences = make([]configv1.TokenAudience, len(item.Issuer.Audiences)) + for index, audience := range item.Issuer.Audiences { + if audience != nil { + provider.Issuer.Audiences[index] = configv1.TokenAudience(*audience) + } + } + if item.Issuer.Ca != nil { + // Slight misuse of the field. It's meant to name a config map holding a + // "ca-bundle.crt" key, whereas we store the data directly in the Name field. + provider.Issuer.CertificateAuthority = configv1.ConfigMapNameReference{ + Name: *item.Issuer.Ca, + } + } + } + + clientSequence := api.DeleteNilsFromPtrSlice(item.Clients) + provider.OIDCClients = make([]configv1.OIDCClientConfig, len(clientSequence)) + for index, client := range clientSequence { + if client.Component != nil { + if client.Component.Name != nil { + provider.OIDCClients[index].ComponentName = *client.Component.Name + } + if client.Component.AuthClientNamespace != nil { + provider.OIDCClients[index].ComponentNamespace = *client.Component.AuthClientNamespace + } + } + if client.ID != nil { + provider.OIDCClients[index].ClientID = *client.ID + } + if client.Secret != nil { + // Slight misuse of the field. It's meant to name a secret holding a + // "clientSecret" key, whereas we store the data directly in the Name field. + provider.OIDCClients[index].ClientSecret.Name = *client.Secret + } + provider.OIDCClients[index].ExtraScopes = api.StringPtrSliceToStringSlice(client.ExtraScopes) + } + + if item.Claim != nil { + if item.Claim.Mappings != nil { + if item.Claim.Mappings.Username != nil { + if item.Claim.Mappings.Username.Claim != nil { + provider.ClaimMappings.Username.TokenClaimMapping.Claim = *item.Claim.Mappings.Username.Claim + } + if item.Claim.Mappings.Username.PrefixPolicy != nil { + provider.ClaimMappings.Username.PrefixPolicy = configv1.UsernamePrefixPolicy(*item.Claim.Mappings.Username.PrefixPolicy) + } + if item.Claim.Mappings.Username.Prefix != nil { + provider.ClaimMappings.Username.Prefix.PrefixString = *item.Claim.Mappings.Username.Prefix + } + } + if item.Claim.Mappings.Groups != nil { + if item.Claim.Mappings.Groups.Claim != nil { + provider.ClaimMappings.Groups.TokenClaimMapping.Claim = *item.Claim.Mappings.Groups.Claim + } + if item.Claim.Mappings.Groups.Prefix != nil { + provider.ClaimMappings.Groups.Prefix = *item.Claim.Mappings.Groups.Prefix + } + } + } + } + + validationRuleSequence := api.DeleteNilsFromPtrSlice(item.Claim.ValidationRules) + provider.ClaimValidationRules = make([]configv1.TokenClaimValidationRule, len(validationRuleSequence)) + for index, rule := range validationRuleSequence { + provider.ClaimValidationRules[index] = configv1.TokenClaimValidationRule{ + Type: configv1.TokenValidationRuleTypeRequiredClaim, + RequiredClaim: &configv1.TokenRequiredClaim{}, + } + if rule.Claim != nil { + provider.ClaimValidationRules[index].RequiredClaim.Claim = *rule.Claim + } + if rule.RequiredValue != nil { + provider.ClaimValidationRules[index].RequiredClaim.RequiredValue = *rule.RequiredValue + } + } + + out.ExternalAuths = append(out.ExternalAuths, provider) + } + + return nil +} + +func (p *IngressProfile) Normalize(out *api.IngressProfile) error { + if p.IP != nil { + text := []byte(*p.IP) + err := out.IP.UnmarshalText(text) + if err != nil { + return err + } + } + if p.URL != nil { + text := []byte(*p.URL) + err := out.URL.UnmarshalBinary(text) + if err != nil { + return err + } + } + if p.Visibility != nil { + text := []byte(*p.Visibility) + err := out.Visibility.UnmarshalText(text) + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/api/v20240610preview/register.go b/internal/api/v20240610preview/register.go new file mode 100644 index 000000000..185347cd6 --- /dev/null +++ b/internal/api/v20240610preview/register.go @@ -0,0 +1,19 @@ +package v20240610preview + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "github.com/Azure/ARO-HCP/internal/api" +) + +type version struct{} + +// String returns the api-version parameter value for this API. +func (v version) String() string { + return "2024-06-10-preview" +} + +func init() { + api.Register(version{}) +} diff --git a/internal/go.mod b/internal/go.mod index a06730a54..c51467b6a 100644 --- a/internal/go.mod +++ b/internal/go.mod @@ -9,6 +9,21 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/openshift/api v0.0.0-20240316014254-8ebde957e3a6 golang.org/x/net v0.21.0 // indirect golang.org/x/text v0.14.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/api v0.29.0 // indirect + k8s.io/apimachinery v0.29.0 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/internal/go.sum b/internal/go.sum index 28ff26ee6..d59ede2ba 100644 --- a/internal/go.sum +++ b/internal/go.sum @@ -2,17 +2,98 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtr github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM= github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ= github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/openshift/api v0.0.0-20240316014254-8ebde957e3a6 h1:G2VumOYEbLx6aBBSZ5czPAw2DWrqZH8bi3qdfbhU/m8= +github.com/openshift/api v0.0.0-20240316014254-8ebde957e3a6/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= +k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= +k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= +k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=