From 43b8b8e6caf16643fa023c4281fd43c0a716c95c Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Mon, 22 Jul 2024 09:40:58 -0400 Subject: [PATCH 1/7] fix provider definition Signed-off-by: Marques Johansson --- cmd/provider/main.go | 74 +++++++++++++++++++++++++++++-------- config/overrides.go | 7 ++++ config/provider.go | 1 + internal/clients/equinix.go | 48 ++++++++++++++++++------ 4 files changed, 104 insertions(+), 26 deletions(-) diff --git a/cmd/provider/main.go b/cmd/provider/main.go index 381a735..314a7d3 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -32,7 +32,6 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/statemetrics" upcontroller "github.com/crossplane/upjet/pkg/controller" - "github.com/crossplane/upjet/pkg/terraform" "gopkg.in/alecthomas/kingpin.v2" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -51,20 +50,37 @@ import ( equinixmetrics "github.com/crossplane-contrib/provider-jet-equinix/internal/metrics" ) +const ( + webhookTLSCertDirEnvVar = "WEBHOOK_TLS_CERT_DIR" + tlsServerCertDirEnvVar = "TLS_SERVER_CERTS_DIR" + certsDirEnvVar = "CERTS_DIR" + tlsServerCertDir = "/tls/server" +) + func main() { var ( - app = kingpin.New(filepath.Base(os.Args[0]), "Terraform based Crossplane provider for Equinix").DefaultEnvars() - debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() - syncInterval = app.Flag("sync", "Sync interval controls how often all resources will be double checked for drift.").Short('s').Default("1h").Duration() - pollInterval = app.Flag("poll", "Poll interval controls how often an individual resource should be checked for drift.").Default("10m").Duration() - leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").OverrideDefaultFromEnvar("LEADER_ELECTION").Bool() - pollStateMetricInterval = app.Flag("poll-state-metric", "State metric recording interval").Default("5s").Duration() - maxReconcileRate = app.Flag("max-reconcile-rate", "The global maximum rate per second at which resources may checked for drift from the desired state.").Default("10").Int() - + app = kingpin.New(filepath.Base(os.Args[0]), "Terraform based Crossplane provider for Equinix").DefaultEnvars() + debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() + syncInterval = app.Flag("sync", "Sync interval controls how often all resources will be double checked for drift.").Short('s').Default("1h").Duration() + pollInterval = app.Flag("poll", "Poll interval controls how often an individual resource should be checked for drift.").Default("10m").Duration() + leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").OverrideDefaultFromEnvar("LEADER_ELECTION").Bool() + terraformVersion = app.Flag("terraform-version", "Terraform version.").Required().Envar("TERRAFORM_VERSION").String() + providerSource = app.Flag("terraform-provider-source", "Terraform provider source.").Required().Envar("TERRAFORM_PROVIDER_SOURCE").String() + providerVersion = app.Flag("terraform-provider-version", "Terraform provider version.").Required().Envar("TERRAFORM_PROVIDER_VERSION").String() + pollStateMetricInterval = app.Flag("poll-state-metric", "State metric recording interval").Default("5s").Duration() + maxReconcileRate = app.Flag("max-reconcile-rate", "The global maximum rate per second at which resources may checked for drift from the desired state.").Default("10").Int() namespace = app.Flag("namespace", "Namespace used to set as default scope in default secret store config.").Default("crossplane-system").Envar("POD_NAMESPACE").String() enableExternalSecretStores = app.Flag("enable-external-secret-stores", "Enable support for ExternalSecretStores.").Default("false").Envar("ENABLE_EXTERNAL_SECRET_STORES").Bool() essTLSCertsPath = app.Flag("ess-tls-cert-dir", "Path of ESS TLS certificates.").Envar("ESS_TLS_CERTS_DIR").String() enableManagementPolicies = app.Flag("enable-management-policies", "Enable support for ManagementPolicies.").Default("true").Envar("ENABLE_MANAGEMENT_POLICIES").Bool() + + certsDirSet = false + // we record whether the command-line option "--certs-dir" was supplied + // in the registered PreAction for the flag. + certsDir = app.Flag("certs-dir", "The directory that contains the server key and certificate.").Default(tlsServerCertDir).Envar(certsDirEnvVar).PreAction(func(_ *kingpin.ParseContext) error { + certsDirSet = true + return nil + }).String() ) kingpin.MustParse(app.Parse(os.Args[1:])) @@ -84,7 +100,30 @@ func main() { cfg, err := ctrl.GetConfig() kingpin.FatalIfError(err, "Cannot get API server rest config") - kingpin.FatalIfError(equinixmetrics.SetupMetrics(), "Cannot setup Linode metrics hook") + + // Get the TLS certs directory from the environment variables set by + // Crossplane if they're available. + // In older XP versions we used WEBHOOK_TLS_CERT_DIR, in newer versions + // we use TLS_SERVER_CERTS_DIR. If an explicit certs dir is not supplied + // via the command-line options, then these environment variables are used + // instead. + if !certsDirSet { + // backwards-compatibility concerns + xpCertsDir := os.Getenv(certsDirEnvVar) + if xpCertsDir == "" { + xpCertsDir = os.Getenv(tlsServerCertDirEnvVar) + } + if xpCertsDir == "" { + xpCertsDir = os.Getenv(webhookTLSCertDirEnvVar) + } + // we probably don't need this condition but just to be on the + // safe side, if we are missing any kingpin machinery details... + if xpCertsDir != "" { + *certsDir = xpCertsDir + } + } + + kingpin.FatalIfError(equinixmetrics.SetupMetrics(), "Cannot setup Equinix metrics hook") mgr, err := ctrl.NewManager(ratelimiter.LimitRESTConfig(cfg, *maxReconcileRate), ctrl.Options{ LeaderElection: *leaderElection, @@ -113,6 +152,13 @@ func main() { ctx := context.Background() provider, err := config.GetProvider(ctx, false) kingpin.FatalIfError(err, "Cannot initialize the provider configuration") + + setupCfg := clients.SetupConfig{ + ProviderVersion: providerVersion, + TerraformVersion: terraformVersion, + ProviderSource: providerSource, + TerraformProvider: provider.TerraformProvider, + } o := upcontroller.Options{ Options: xpcontroller.Options{ Logger: log, @@ -122,13 +168,11 @@ func main() { Features: &feature.Flags{}, MetricOptions: &mo, }, - Provider: provider, - // use the following WorkspaceStoreOption to enable the shared gRPC mode - // terraform.WithProviderRunner(terraform.NewSharedProvider(log, os.Getenv("TERRAFORM_NATIVE_PROVIDER_PATH"), terraform.WithNativeProviderArgs("-debuggable"))) - WorkspaceStore: terraform.NewWorkspaceStore(log), - SetupFn: clients.TerraformSetupBuilder(provider.TerraformProvider), + Provider: provider, + SetupFn: clients.TerraformSetupBuilder(setupCfg), PollJitter: pollJitter, OperationTrackerStore: upcontroller.NewOperationStore(log), + StartWebhooks: *certsDir != "", } if *enableManagementPolicies { diff --git a/config/overrides.go b/config/overrides.go index 6f748a5..f31c204 100644 --- a/config/overrides.go +++ b/config/overrides.go @@ -34,6 +34,13 @@ func IdentifierAssignedByEquinix() upconfig.ResourceOption { } } +// LongProvision will set the resource to be provisioned asynchronously. Use this for resources with >1m provisions +func LongProvision() upconfig.ResourceOption { + return func(r *upconfig.Resource) { + r.UseAsync = true + } +} + var knownReferencerTFResource = map[string]map[string]string{ "metal": { "project_id": "equinix_metal_project", diff --git a/config/provider.go b/config/provider.go index e8c086c..ff74076 100644 --- a/config/provider.go +++ b/config/provider.go @@ -106,6 +106,7 @@ func GetProvider(_ context.Context, generationProvider bool) (*upconfig.Provider KnownReferencers(), IdentifierAssignedByEquinix(), SkipOptCompLateInitialization(), + LongProvision(), // TODO: use this only for Device and other long-provisioning resources ), ) diff --git a/internal/clients/equinix.go b/internal/clients/equinix.go index bda40e0..e5144ff 100644 --- a/internal/clients/equinix.go +++ b/internal/clients/equinix.go @@ -21,13 +21,16 @@ import ( "encoding/json" "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/upjet/pkg/terraform" + equinixprovider "github.com/equinix/terraform-provider-equinix/equinix/provider" + + "github.com/equinix/terraform-provider-equinix/version" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + terraformsdk "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/crossplane/upjet/pkg/terraform" - "github.com/crossplane-contrib/provider-jet-equinix/apis/v1alpha1" ) @@ -50,12 +53,10 @@ const ( ) type SetupConfig struct { - NativeProviderPath *string - NativeProviderSource *string - NativeProviderVersion *string - TerraformVersion *string - DefaultScheduler terraform.ProviderScheduler - TerraformProvider *schema.Provider + ProviderSource *string + ProviderVersion *string + TerraformVersion *string + TerraformProvider *schema.Provider } func prepareTerraformProviderConfiguration(creds map[string]string, pc v1alpha1.ProviderConfiguration) map[string]any { @@ -85,9 +86,15 @@ func prepareTerraformProviderConfiguration(creds map[string]string, pc v1alpha1. // TerraformSetupBuilder builds Terraform a terraform.SetupFn function which // returns Terraform provider setup configuration -func TerraformSetupBuilder(tfProvider *schema.Provider) terraform.SetupFn { +func TerraformSetupBuilder(setupCfg SetupConfig) terraform.SetupFn { return func(ctx context.Context, client client.Client, mg resource.Managed) (terraform.Setup, error) { - ps := terraform.Setup{} + ps := terraform.Setup{ + Version: *setupCfg.TerraformVersion, + Requirement: terraform.ProviderRequirement{ + Source: *setupCfg.ProviderSource, + Version: *setupCfg.ProviderVersion, + }, + } configRef := mg.GetProviderConfigReference() if configRef == nil { @@ -113,6 +120,25 @@ func TerraformSetupBuilder(tfProvider *schema.Provider) terraform.SetupFn { } ps.Configuration = prepareTerraformProviderConfiguration(equinixCreds, pc.Spec.Configuration) - return ps, nil + return ps, errors.Wrap(configureNoForkEquinixClient(ctx, &ps, *setupCfg.TerraformProvider), "failed to configure the no-fork equinix client") } } + +func configureNoForkEquinixClient(ctx context.Context, ps *terraform.Setup, p schema.Provider) error { + // Please be aware that this implementation relies on the schema.Provider + // parameter `p` being a non-pointer. This is because normally + // the Terraform plugin SDK normally configures the provider + // only once and using a pointer argument here will cause + // race conditions between resources referring to different + // ProviderConfigs. + diag := p.Configure(context.WithoutCancel(ctx), &terraformsdk.ResourceConfig{ + Config: ps.Configuration, + }) + if diag != nil && diag.HasError() { + return errors.Errorf("failed to configure the provider: %v", diag) + } + + fwProvider := equinixprovider.CreateFrameworkProvider(version.ProviderVersion) + ps.FrameworkProvider = fwProvider + return nil +} From 88ac730e89d6366c0dfcca7a505b02ffbec1c42e Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Tue, 23 Jul 2024 21:51:18 -0400 Subject: [PATCH 2/7] feat: update providerconfig to v1beta1 Signed-off-by: Marques Johansson --- apis/v1beta1/doc.go | 21 ++ apis/v1beta1/register.go | 64 ++++++ apis/v1beta1/types.go | 120 ++++++++++ apis/v1beta1/zz_generated.deepcopy.go | 206 ++++++++++++++++++ apis/v1beta1/zz_generated.pc.go | 40 ++++ apis/v1beta1/zz_generated.pcu.go | 40 ++++ apis/v1beta1/zz_generated.pculist.go | 29 +++ apis/zz_register.go | 2 + config/provider.go | 10 - internal/clients/equinix.go | 8 +- internal/controller/providerconfig/config.go | 12 +- ...nix.jet.crossplane.io_providerconfigs.yaml | 184 +++++++++++++++- ...et.crossplane.io_providerconfigusages.yaml | 101 ++++++++- 13 files changed, 814 insertions(+), 23 deletions(-) create mode 100644 apis/v1beta1/doc.go create mode 100644 apis/v1beta1/register.go create mode 100644 apis/v1beta1/types.go create mode 100644 apis/v1beta1/zz_generated.deepcopy.go create mode 100644 apis/v1beta1/zz_generated.pc.go create mode 100644 apis/v1beta1/zz_generated.pcu.go create mode 100644 apis/v1beta1/zz_generated.pculist.go diff --git a/apis/v1beta1/doc.go b/apis/v1beta1/doc.go new file mode 100644 index 0000000..0c5205f --- /dev/null +++ b/apis/v1beta1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2024 The Crossplane Authors. + +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 v1beta1 contains the core resources of the template upjet provider. +// +kubebuilder:object:generate=true +// +groupName=equinix.jet.crossplane.io +// +versionName=v1beta1 +package v1beta1 diff --git a/apis/v1beta1/register.go b/apis/v1beta1/register.go new file mode 100644 index 0000000..d99f754 --- /dev/null +++ b/apis/v1beta1/register.go @@ -0,0 +1,64 @@ +/* +Copyright 2024 The Crossplane Authors. + +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 v1beta1 + +import ( + "reflect" + + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "equinix.jet.crossplane.io" + Version = "v1beta1" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +) + +// ProviderConfig type metadata. +var ( + ProviderConfigKind = reflect.TypeOf(ProviderConfig{}).Name() + ProviderConfigGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigKind}.String() + ProviderConfigKindAPIVersion = ProviderConfigKind + "." + SchemeGroupVersion.String() + ProviderConfigGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigKind) +) + +// ProviderConfigUsage type metadata. +var ( + ProviderConfigUsageKind = reflect.TypeOf(ProviderConfigUsage{}).Name() + ProviderConfigUsageGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigUsageKind}.String() + ProviderConfigUsageKindAPIVersion = ProviderConfigUsageKind + "." + SchemeGroupVersion.String() + ProviderConfigUsageGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigUsageKind) + + ProviderConfigUsageListKind = reflect.TypeOf(ProviderConfigUsageList{}).Name() + ProviderConfigUsageListGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigUsageListKind}.String() + ProviderConfigUsageListKindAPIVersion = ProviderConfigUsageListKind + "." + SchemeGroupVersion.String() + ProviderConfigUsageListGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigUsageListKind) +) + +func init() { + SchemeBuilder.Register(&ProviderConfig{}, &ProviderConfigList{}) + SchemeBuilder.Register(&ProviderConfigUsage{}, &ProviderConfigUsageList{}) +} diff --git a/apis/v1beta1/types.go b/apis/v1beta1/types.go new file mode 100644 index 0000000..3f4e452 --- /dev/null +++ b/apis/v1beta1/types.go @@ -0,0 +1,120 @@ +/* +Copyright 2024 The Crossplane Authors. + +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 v1beta1 + +import ( + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// A ProviderConfigSpec defines the desired state of a ProviderConfig. +type ProviderConfigSpec struct { + // Credentials required to authenticate to this provider. + Credentials ProviderCredentials `json:"credentials"` + + // +kubebuilder:validation:Optional + Configuration ProviderConfiguration `json:"config"` +} + +// ProviderConfiguration for configuring the terraform provider +// see https://registry.terraform.io/providers/equinix/equinix/latest/docs#configuration-reference +type ProviderConfiguration struct { + // The Equinix API base URL to point out desired environment. This argument can also be specified with the EQUINIX_API_ENDPOINT shell environment variable. (Defaults to https://api.equinix.com) + // +kubebuilder:validation:Optional + Endpoint string `json:"endpoint"` + + // Maximum number of retries in case of network failure. + // +kubebuilder:validation:Optional + MaxRetries bool `json:"max_retries"` + + // Maximum number of seconds to wait before retrying a request. + // +kubebuilder:validation:Optional + MaxRetryWaitSeconds bool `json:"max_retry_wait_seconds"` + + // The duration of time, in seconds, that the Equinix Platform API Client should wait before canceling an API request. Canceled requests may still result in provisioned resources. (Defaults to 30) + // +kubebuilder:validation:Optional + RequestTimeout bool `json:"request_timeout"` + + // The maximum number of records in a single response for REST queries that produce paginated responses. (Default is client specific) + // +kubebuilder:validation:Optional + ResponseMaxPageSize bool `json:"response_max_page_size"` +} + +// ProviderCredentials required to authenticate. +type ProviderCredentials struct { + // Source of the provider credentials. + // +kubebuilder:validation:Enum=None;Secret;InjectedIdentity;Environment;Filesystem + Source xpv1.CredentialsSource `json:"source"` + + xpv1.CommonCredentialSelectors `json:",inline"` +} + +// A ProviderConfigStatus reflects the observed state of a ProviderConfig. +type ProviderConfigStatus struct { + xpv1.ProviderConfigStatus `json:",inline"` +} + +// +kubebuilder:object:root=true + +// A ProviderConfig configures a Linode provider. +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="SECRET-NAME",type="string",JSONPath=".spec.credentials.secretRef.name",priority=1 +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:resource:scope=Cluster,categories={crossplane,providerconfig,equinix} +// +kubebuilder:storageversion +type ProviderConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ProviderConfigSpec `json:"spec"` + Status ProviderConfigStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ProviderConfigList contains a list of ProviderConfig. +type ProviderConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ProviderConfig `json:"items"` +} + +// +kubebuilder:object:root=true + +// A ProviderConfigUsage indicates that a resource is using a ProviderConfig. +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="CONFIG-NAME",type="string",JSONPath=".providerConfigRef.name" +// +kubebuilder:printcolumn:name="RESOURCE-KIND",type="string",JSONPath=".resourceRef.kind" +// +kubebuilder:printcolumn:name="RESOURCE-NAME",type="string",JSONPath=".resourceRef.name" +// +kubebuilder:resource:scope=Cluster,categories={crossplane,providerconfig,equinix} +// +kubebuilder:storageversion +type ProviderConfigUsage struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + xpv1.ProviderConfigUsage `json:",inline"` +} + +// +kubebuilder:object:root=true + +// ProviderConfigUsageList contains a list of ProviderConfigUsage +type ProviderConfigUsageList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ProviderConfigUsage `json:"items"` +} diff --git a/apis/v1beta1/zz_generated.deepcopy.go b/apis/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000..dfaf651 --- /dev/null +++ b/apis/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,206 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2021 The Crossplane Authors. + +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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +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 *ProviderConfig) DeepCopyInto(out *ProviderConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfig. +func (in *ProviderConfig) DeepCopy() *ProviderConfig { + if in == nil { + return nil + } + out := new(ProviderConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProviderConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigList) DeepCopyInto(out *ProviderConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ProviderConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigList. +func (in *ProviderConfigList) DeepCopy() *ProviderConfigList { + if in == nil { + return nil + } + out := new(ProviderConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProviderConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigSpec) DeepCopyInto(out *ProviderConfigSpec) { + *out = *in + in.Credentials.DeepCopyInto(&out.Credentials) + out.Configuration = in.Configuration +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigSpec. +func (in *ProviderConfigSpec) DeepCopy() *ProviderConfigSpec { + if in == nil { + return nil + } + out := new(ProviderConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigStatus) DeepCopyInto(out *ProviderConfigStatus) { + *out = *in + in.ProviderConfigStatus.DeepCopyInto(&out.ProviderConfigStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigStatus. +func (in *ProviderConfigStatus) DeepCopy() *ProviderConfigStatus { + if in == nil { + return nil + } + out := new(ProviderConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigUsage) DeepCopyInto(out *ProviderConfigUsage) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.ProviderConfigUsage.DeepCopyInto(&out.ProviderConfigUsage) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigUsage. +func (in *ProviderConfigUsage) DeepCopy() *ProviderConfigUsage { + if in == nil { + return nil + } + out := new(ProviderConfigUsage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProviderConfigUsage) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigUsageList) DeepCopyInto(out *ProviderConfigUsageList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ProviderConfigUsage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigUsageList. +func (in *ProviderConfigUsageList) DeepCopy() *ProviderConfigUsageList { + if in == nil { + return nil + } + out := new(ProviderConfigUsageList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProviderConfigUsageList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfiguration) DeepCopyInto(out *ProviderConfiguration) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfiguration. +func (in *ProviderConfiguration) DeepCopy() *ProviderConfiguration { + if in == nil { + return nil + } + out := new(ProviderConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderCredentials) DeepCopyInto(out *ProviderCredentials) { + *out = *in + in.CommonCredentialSelectors.DeepCopyInto(&out.CommonCredentialSelectors) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderCredentials. +func (in *ProviderCredentials) DeepCopy() *ProviderCredentials { + if in == nil { + return nil + } + out := new(ProviderCredentials) + in.DeepCopyInto(out) + return out +} diff --git a/apis/v1beta1/zz_generated.pc.go b/apis/v1beta1/zz_generated.pc.go new file mode 100644 index 0000000..c8d18e3 --- /dev/null +++ b/apis/v1beta1/zz_generated.pc.go @@ -0,0 +1,40 @@ +/* +Copyright 2021 The Crossplane Authors. + +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. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1beta1 + +import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + +// GetCondition of this ProviderConfig. +func (p *ProviderConfig) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return p.Status.GetCondition(ct) +} + +// GetUsers of this ProviderConfig. +func (p *ProviderConfig) GetUsers() int64 { + return p.Status.Users +} + +// SetConditions of this ProviderConfig. +func (p *ProviderConfig) SetConditions(c ...xpv1.Condition) { + p.Status.SetConditions(c...) +} + +// SetUsers of this ProviderConfig. +func (p *ProviderConfig) SetUsers(i int64) { + p.Status.Users = i +} diff --git a/apis/v1beta1/zz_generated.pcu.go b/apis/v1beta1/zz_generated.pcu.go new file mode 100644 index 0000000..ebde1a9 --- /dev/null +++ b/apis/v1beta1/zz_generated.pcu.go @@ -0,0 +1,40 @@ +/* +Copyright 2021 The Crossplane Authors. + +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. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1beta1 + +import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + +// GetProviderConfigReference of this ProviderConfigUsage. +func (p *ProviderConfigUsage) GetProviderConfigReference() xpv1.Reference { + return p.ProviderConfigReference +} + +// GetResourceReference of this ProviderConfigUsage. +func (p *ProviderConfigUsage) GetResourceReference() xpv1.TypedReference { + return p.ResourceReference +} + +// SetProviderConfigReference of this ProviderConfigUsage. +func (p *ProviderConfigUsage) SetProviderConfigReference(r xpv1.Reference) { + p.ProviderConfigReference = r +} + +// SetResourceReference of this ProviderConfigUsage. +func (p *ProviderConfigUsage) SetResourceReference(r xpv1.TypedReference) { + p.ResourceReference = r +} diff --git a/apis/v1beta1/zz_generated.pculist.go b/apis/v1beta1/zz_generated.pculist.go new file mode 100644 index 0000000..ef9c07d --- /dev/null +++ b/apis/v1beta1/zz_generated.pculist.go @@ -0,0 +1,29 @@ +/* +Copyright 2021 The Crossplane Authors. + +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. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1beta1 + +import resource "github.com/crossplane/crossplane-runtime/pkg/resource" + +// GetItems of this ProviderConfigUsageList. +func (p *ProviderConfigUsageList) GetItems() []resource.ProviderConfigUsage { + items := make([]resource.ProviderConfigUsage, len(p.Items)) + for i := range p.Items { + items[i] = &p.Items[i] + } + return items +} diff --git a/apis/zz_register.go b/apis/zz_register.go index b90ad9d..7e303dd 100755 --- a/apis/zz_register.go +++ b/apis/zz_register.go @@ -26,6 +26,7 @@ import ( v1alpha1metal "github.com/crossplane-contrib/provider-jet-equinix/apis/metal/v1alpha1" v1alpha1network "github.com/crossplane-contrib/provider-jet-equinix/apis/network/v1alpha1" v1alpha1apis "github.com/crossplane-contrib/provider-jet-equinix/apis/v1alpha1" + v1beta1 "github.com/crossplane-contrib/provider-jet-equinix/apis/v1beta1" ) func init() { @@ -35,6 +36,7 @@ func init() { v1alpha1metal.SchemeBuilder.AddToScheme, v1alpha1network.SchemeBuilder.AddToScheme, v1alpha1apis.SchemeBuilder.AddToScheme, + v1beta1.SchemeBuilder.AddToScheme, ) } diff --git a/config/provider.go b/config/provider.go index ff74076..2225894 100644 --- a/config/provider.go +++ b/config/provider.go @@ -92,16 +92,6 @@ func GetProvider(_ context.Context, generationProvider bool) (*upconfig.Provider // upconfig.WithSkipList([]string{".*"}), // helpful when debugging to minimize the number of resources // config.WithTerraformPluginSDKIncludeList(resourceList(terraformSDKIncludeList)), // config.WithTerraformPluginFrameworkIncludeList(resourceList(terraformPluginFrameworkExternalNameConfigs)), - upconfig.WithBasePackages(upconfig.BasePackages{ - APIVersion: []string{ - // Default package for ProviderConfig APIs - "apis/v1alpha1", - }, - Controller: []string{ - // Default package for ProviderConfig controllers - "internal/controller/providerconfig", - }, - }), upconfig.WithDefaultResourceOptions( KnownReferencers(), IdentifierAssignedByEquinix(), diff --git a/internal/clients/equinix.go b/internal/clients/equinix.go index e5144ff..c589657 100644 --- a/internal/clients/equinix.go +++ b/internal/clients/equinix.go @@ -31,7 +31,7 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/crossplane-contrib/provider-jet-equinix/apis/v1alpha1" + "github.com/crossplane-contrib/provider-jet-equinix/apis/v1beta1" ) const ( @@ -59,7 +59,7 @@ type SetupConfig struct { TerraformProvider *schema.Provider } -func prepareTerraformProviderConfiguration(creds map[string]string, pc v1alpha1.ProviderConfiguration) map[string]any { +func prepareTerraformProviderConfiguration(creds map[string]string, pc v1beta1.ProviderConfiguration) map[string]any { config := map[string]any{} config[keyMaxRetries] = pc.MaxRetries config[keyMaxRetryWaitSeconds] = pc.MaxRetryWaitSeconds @@ -100,12 +100,12 @@ func TerraformSetupBuilder(setupCfg SetupConfig) terraform.SetupFn { if configRef == nil { return ps, errors.New(errNoProviderConfig) } - pc := &v1alpha1.ProviderConfig{} + pc := &v1beta1.ProviderConfig{} if err := client.Get(ctx, types.NamespacedName{Name: configRef.Name}, pc); err != nil { return ps, errors.Wrap(err, errGetProviderConfig) } - t := resource.NewProviderConfigUsageTracker(client, &v1alpha1.ProviderConfigUsage{}) + t := resource.NewProviderConfigUsageTracker(client, &v1beta1.ProviderConfigUsage{}) if err := t.Track(ctx, mg); err != nil { return ps, errors.Wrap(err, errTrackUsage) } diff --git a/internal/controller/providerconfig/config.go b/internal/controller/providerconfig/config.go index f25f494..8180b42 100644 --- a/internal/controller/providerconfig/config.go +++ b/internal/controller/providerconfig/config.go @@ -24,24 +24,24 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/upjet/pkg/controller" - "github.com/crossplane-contrib/provider-jet-equinix/apis/v1alpha1" + "github.com/crossplane-contrib/provider-jet-equinix/apis/v1beta1" ) // Setup adds a controller that reconciles ProviderConfigs by accounting for // their current usage. func Setup(mgr ctrl.Manager, o controller.Options) error { - name := providerconfig.ControllerName(v1alpha1.ProviderConfigGroupKind) + name := providerconfig.ControllerName(v1beta1.ProviderConfigGroupKind) of := resource.ProviderConfigKinds{ - Config: v1alpha1.ProviderConfigGroupVersionKind, - UsageList: v1alpha1.ProviderConfigUsageListGroupVersionKind, + Config: v1beta1.ProviderConfigGroupVersionKind, + UsageList: v1beta1.ProviderConfigUsageListGroupVersionKind, } return ctrl.NewControllerManagedBy(mgr). Named(name). WithOptions(o.ForControllerRuntime()). - For(&v1alpha1.ProviderConfig{}). - Watches(&v1alpha1.ProviderConfigUsage{}, &resource.EnqueueRequestForProviderConfig{}). + For(&v1beta1.ProviderConfig{}). + Watches(&v1beta1.ProviderConfigUsage{}, &resource.EnqueueRequestForProviderConfig{}). Complete(providerconfig.NewReconciler(mgr, of, providerconfig.WithLogger(o.Logger.WithValues("controller", name)), providerconfig.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))))) diff --git a/package/crds/equinix.jet.crossplane.io_providerconfigs.yaml b/package/crds/equinix.jet.crossplane.io_providerconfigs.yaml index d1340e1..287296b 100644 --- a/package/crds/equinix.jet.crossplane.io_providerconfigs.yaml +++ b/package/crds/equinix.jet.crossplane.io_providerconfigs.yaml @@ -11,7 +11,7 @@ spec: categories: - crossplane - providerconfig - - equinixjet + - equinix kind: ProviderConfig listKind: ProviderConfigList plural: providerconfigs @@ -197,6 +197,188 @@ spec: - spec type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .spec.credentials.secretRef.name + name: SECRET-NAME + priority: 1 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: A ProviderConfig configures a Linode provider. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: A ProviderConfigSpec defines the desired state of a ProviderConfig. + properties: + config: + description: |- + ProviderConfiguration for configuring the terraform provider + see https://registry.terraform.io/providers/equinix/equinix/latest/docs#configuration-reference + properties: + endpoint: + description: The Equinix API base URL to point out desired environment. + This argument can also be specified with the EQUINIX_API_ENDPOINT + shell environment variable. (Defaults to https://api.equinix.com) + type: string + max_retries: + description: Maximum number of retries in case of network failure. + type: boolean + max_retry_wait_seconds: + description: Maximum number of seconds to wait before retrying + a request. + type: boolean + request_timeout: + description: The duration of time, in seconds, that the Equinix + Platform API Client should wait before canceling an API request. + Canceled requests may still result in provisioned resources. + (Defaults to 30) + type: boolean + response_max_page_size: + description: The maximum number of records in a single response + for REST queries that produce paginated responses. (Default + is client specific) + type: boolean + type: object + credentials: + description: Credentials required to authenticate to this provider. + properties: + env: + description: |- + Env is a reference to an environment variable that contains credentials + that must be used to connect to the provider. + properties: + name: + description: Name is the name of an environment variable. + type: string + required: + - name + type: object + fs: + description: |- + Fs is a reference to a filesystem location that contains credentials that + must be used to connect to the provider. + properties: + path: + description: Path is a filesystem path. + type: string + required: + - path + type: object + secretRef: + description: |- + A SecretRef is a reference to a secret key that contains the credentials + that must be used to connect to the provider. + properties: + key: + description: The key to select. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - key + - name + - namespace + type: object + source: + description: Source of the provider credentials. + enum: + - None + - Secret + - InjectedIdentity + - Environment + - Filesystem + type: string + required: + - source + type: object + required: + - credentials + type: object + status: + description: A ProviderConfigStatus reflects the observed state of a ProviderConfig. + properties: + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time this condition transitioned from one + status to another. + format: date-time + type: string + message: + description: |- + A Message containing details about this condition's last transition from + one status to another, if any. + type: string + observedGeneration: + description: |- + ObservedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + type: integer + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: |- + Type of this condition. At most one of each condition type may apply to + a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + users: + description: Users of this provider configuration. + format: int64 + type: integer + type: object + required: + - spec + type: object + served: true storage: true subresources: status: {} diff --git a/package/crds/equinix.jet.crossplane.io_providerconfigusages.yaml b/package/crds/equinix.jet.crossplane.io_providerconfigusages.yaml index 9e640d0..687f11f 100644 --- a/package/crds/equinix.jet.crossplane.io_providerconfigusages.yaml +++ b/package/crds/equinix.jet.crossplane.io_providerconfigusages.yaml @@ -10,8 +10,8 @@ spec: names: categories: - crossplane - - provider - - equinixjet + - providerconfig + - equinix kind: ProviderConfigUsage listKind: ProviderConfigUsageList plural: providerconfigusages @@ -113,5 +113,102 @@ spec: - resourceRef type: object served: true + storage: false + subresources: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .providerConfigRef.name + name: CONFIG-NAME + type: string + - jsonPath: .resourceRef.kind + name: RESOURCE-KIND + type: string + - jsonPath: .resourceRef.name + name: RESOURCE-NAME + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: A ProviderConfigUsage indicates that a resource is using a ProviderConfig. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + providerConfigRef: + description: ProviderConfigReference to the provider config being used. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + resourceRef: + description: ResourceReference to the managed resource using the provider + config. + properties: + apiVersion: + description: APIVersion of the referenced object. + type: string + kind: + description: Kind of the referenced object. + type: string + name: + description: Name of the referenced object. + type: string + uid: + description: UID of the referenced object. + type: string + required: + - apiVersion + - kind + - name + type: object + required: + - providerConfigRef + - resourceRef + type: object + served: true storage: true subresources: {} From 0071808981105db4dee08e8da9cf339f28838656 Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Sun, 28 Jul 2024 10:28:40 -0400 Subject: [PATCH 3/7] rename NoFork to TerraformPluginSDK Signed-off-by: Marques Johansson --- internal/clients/equinix.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/clients/equinix.go b/internal/clients/equinix.go index c589657..38e72df 100644 --- a/internal/clients/equinix.go +++ b/internal/clients/equinix.go @@ -120,11 +120,11 @@ func TerraformSetupBuilder(setupCfg SetupConfig) terraform.SetupFn { } ps.Configuration = prepareTerraformProviderConfiguration(equinixCreds, pc.Spec.Configuration) - return ps, errors.Wrap(configureNoForkEquinixClient(ctx, &ps, *setupCfg.TerraformProvider), "failed to configure the no-fork equinix client") + return ps, errors.Wrap(configureTerraformPluginSDKEquinixClient(ctx, &ps, *setupCfg.TerraformProvider), "failed to configure the Terraform Plugin SDK Equinix client") } } -func configureNoForkEquinixClient(ctx context.Context, ps *terraform.Setup, p schema.Provider) error { +func configureTerraformPluginSDKEquinixClient(ctx context.Context, ps *terraform.Setup, p schema.Provider) error { // Please be aware that this implementation relies on the schema.Provider // parameter `p` being a non-pointer. This is because normally // the Terraform plugin SDK normally configures the provider From 9095a6ac334a82a28683e5d73f4d8f6bab19aa5b Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Sun, 28 Jul 2024 19:27:37 -0400 Subject: [PATCH 4/7] fix: equinix providerconfig needs int for retry timeout, page_size, and retry settings Signed-off-by: Marques Johansson --- apis/v1alpha1/types.go | 6 +++--- apis/v1beta1/types.go | 8 ++++---- apis/zz_register.go | 2 -- .../crds/equinix.jet.crossplane.io_providerconfigs.yaml | 8 ++++---- package/crds/equinix.jet.crossplane.io_storeconfigs.yaml | 2 +- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/apis/v1alpha1/types.go b/apis/v1alpha1/types.go index a4a34ad..60e3723 100644 --- a/apis/v1alpha1/types.go +++ b/apis/v1alpha1/types.go @@ -76,7 +76,7 @@ type ProviderConfigStatus struct { // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" // +kubebuilder:printcolumn:name="SECRET-NAME",type="string",JSONPath=".spec.credentials.secretRef.name",priority=1 // +kubebuilder:resource:scope=Cluster -// +kubebuilder:resource:scope=Cluster,categories={crossplane,providerconfig,equinixjet} +// +kubebuilder:resource:scope=Cluster,categories={crossplane,providerconfig,equinix} type ProviderConfig struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -101,7 +101,7 @@ type ProviderConfigList struct { // +kubebuilder:printcolumn:name="CONFIG-NAME",type="string",JSONPath=".providerConfigRef.name" // +kubebuilder:printcolumn:name="RESOURCE-KIND",type="string",JSONPath=".resourceRef.kind" // +kubebuilder:printcolumn:name="RESOURCE-NAME",type="string",JSONPath=".resourceRef.name" -// +kubebuilder:resource:scope=Cluster,categories={crossplane,provider,equinixjet} +// +kubebuilder:resource:scope=Cluster,categories={crossplane,provider,equinix} type ProviderConfigUsage struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -134,7 +134,7 @@ type StoreConfigStatus struct { // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" // +kubebuilder:printcolumn:name="TYPE",type="string",JSONPath=".spec.type" // +kubebuilder:printcolumn:name="DEFAULT-SCOPE",type="string",JSONPath=".spec.defaultScope" -// +kubebuilder:resource:scope=Cluster,categories={crossplane,store,gcp} +// +kubebuilder:resource:scope=Cluster,categories={crossplane,store,equinix} // +kubebuilder:subresource:status type StoreConfig struct { metav1.TypeMeta `json:",inline"` diff --git a/apis/v1beta1/types.go b/apis/v1beta1/types.go index 3f4e452..fa7a572 100644 --- a/apis/v1beta1/types.go +++ b/apis/v1beta1/types.go @@ -39,19 +39,19 @@ type ProviderConfiguration struct { // Maximum number of retries in case of network failure. // +kubebuilder:validation:Optional - MaxRetries bool `json:"max_retries"` + MaxRetries int `json:"max_retries"` // Maximum number of seconds to wait before retrying a request. // +kubebuilder:validation:Optional - MaxRetryWaitSeconds bool `json:"max_retry_wait_seconds"` + MaxRetryWaitSeconds int `json:"max_retry_wait_seconds"` // The duration of time, in seconds, that the Equinix Platform API Client should wait before canceling an API request. Canceled requests may still result in provisioned resources. (Defaults to 30) // +kubebuilder:validation:Optional - RequestTimeout bool `json:"request_timeout"` + RequestTimeout int `json:"request_timeout"` // The maximum number of records in a single response for REST queries that produce paginated responses. (Default is client specific) // +kubebuilder:validation:Optional - ResponseMaxPageSize bool `json:"response_max_page_size"` + ResponseMaxPageSize int `json:"response_max_page_size"` } // ProviderCredentials required to authenticate. diff --git a/apis/zz_register.go b/apis/zz_register.go index 7e303dd..e8babdd 100755 --- a/apis/zz_register.go +++ b/apis/zz_register.go @@ -25,7 +25,6 @@ import ( v1alpha1 "github.com/crossplane-contrib/provider-jet-equinix/apis/fabric/v1alpha1" v1alpha1metal "github.com/crossplane-contrib/provider-jet-equinix/apis/metal/v1alpha1" v1alpha1network "github.com/crossplane-contrib/provider-jet-equinix/apis/network/v1alpha1" - v1alpha1apis "github.com/crossplane-contrib/provider-jet-equinix/apis/v1alpha1" v1beta1 "github.com/crossplane-contrib/provider-jet-equinix/apis/v1beta1" ) @@ -35,7 +34,6 @@ func init() { v1alpha1.SchemeBuilder.AddToScheme, v1alpha1metal.SchemeBuilder.AddToScheme, v1alpha1network.SchemeBuilder.AddToScheme, - v1alpha1apis.SchemeBuilder.AddToScheme, v1beta1.SchemeBuilder.AddToScheme, ) } diff --git a/package/crds/equinix.jet.crossplane.io_providerconfigs.yaml b/package/crds/equinix.jet.crossplane.io_providerconfigs.yaml index 287296b..d00c0da 100644 --- a/package/crds/equinix.jet.crossplane.io_providerconfigs.yaml +++ b/package/crds/equinix.jet.crossplane.io_providerconfigs.yaml @@ -245,22 +245,22 @@ spec: type: string max_retries: description: Maximum number of retries in case of network failure. - type: boolean + type: integer max_retry_wait_seconds: description: Maximum number of seconds to wait before retrying a request. - type: boolean + type: integer request_timeout: description: The duration of time, in seconds, that the Equinix Platform API Client should wait before canceling an API request. Canceled requests may still result in provisioned resources. (Defaults to 30) - type: boolean + type: integer response_max_page_size: description: The maximum number of records in a single response for REST queries that produce paginated responses. (Default is client specific) - type: boolean + type: integer type: object credentials: description: Credentials required to authenticate to this provider. diff --git a/package/crds/equinix.jet.crossplane.io_storeconfigs.yaml b/package/crds/equinix.jet.crossplane.io_storeconfigs.yaml index defe717..e344d24 100644 --- a/package/crds/equinix.jet.crossplane.io_storeconfigs.yaml +++ b/package/crds/equinix.jet.crossplane.io_storeconfigs.yaml @@ -11,7 +11,7 @@ spec: categories: - crossplane - store - - gcp + - equinix kind: StoreConfig listKind: StoreConfigList plural: storeconfigs From 78637d8580c282ec587f47afc5cb3235d4e101ba Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Sun, 28 Jul 2024 19:28:55 -0400 Subject: [PATCH 5/7] fix: define terraform.Setup with working workspace, scheduler, and TF provider definition values Signed-off-by: Marques Johansson --- cmd/provider/main.go | 51 +++++++++++++++++++++++++++---------- config/provider.go | 10 ++++++++ internal/clients/equinix.go | 11 +++++--- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/cmd/provider/main.go b/cmd/provider/main.go index 314a7d3..408eb48 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -32,6 +32,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/statemetrics" upcontroller "github.com/crossplane/upjet/pkg/controller" + "github.com/crossplane/upjet/pkg/terraform" "gopkg.in/alecthomas/kingpin.v2" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -57,16 +58,23 @@ const ( tlsServerCertDir = "/tls/server" ) +func ptr[T any](s T) *T { return &s } + func main() { var ( - app = kingpin.New(filepath.Base(os.Args[0]), "Terraform based Crossplane provider for Equinix").DefaultEnvars() - debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() - syncInterval = app.Flag("sync", "Sync interval controls how often all resources will be double checked for drift.").Short('s').Default("1h").Duration() - pollInterval = app.Flag("poll", "Poll interval controls how often an individual resource should be checked for drift.").Default("10m").Duration() - leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").OverrideDefaultFromEnvar("LEADER_ELECTION").Bool() - terraformVersion = app.Flag("terraform-version", "Terraform version.").Required().Envar("TERRAFORM_VERSION").String() - providerSource = app.Flag("terraform-provider-source", "Terraform provider source.").Required().Envar("TERRAFORM_PROVIDER_SOURCE").String() - providerVersion = app.Flag("terraform-provider-version", "Terraform provider version.").Required().Envar("TERRAFORM_PROVIDER_VERSION").String() + app = kingpin.New(filepath.Base(os.Args[0]), "Terraform based Crossplane provider for Equinix").DefaultEnvars() + debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() + syncInterval = app.Flag("sync", "Sync interval controls how often all resources will be double checked for drift.").Short('s').Default("1h").Duration() + pollInterval = app.Flag("poll", "Poll interval controls how often an individual resource should be checked for drift.").Default("10m").Duration() + leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").OverrideDefaultFromEnvar("LEADER_ELECTION").Bool() + // terraformVersion = app.Flag("terraform-version", "Terraform version.").Required().Envar("TERRAFORM_VERSION").String() + _ = app.Flag("terraform-version", "Terraform version.").Required().Envar("TERRAFORM_VERSION").String() + // providerSource = app.Flag("terraform-provider-source", "Terraform provider source.").Required().Envar("TERRAFORM_PROVIDER_SOURCE").String() + _ = app.Flag("terraform-provider-source", "Terraform provider source.").Required().Envar("TERRAFORM_PROVIDER_SOURCE").String() + // providerVersion = app.Flag("terraform-provider-version", "Terraform provider version.").Required().Envar("TERRAFORM_PROVIDER_VERSION").String() + _ = app.Flag("terraform-provider-version", "Terraform provider version.").Required().Envar("TERRAFORM_PROVIDER_VERSION").String() + nativeProviderPath = app.Flag("terraform-native-provider-path", "Terraform native provider path for shared execution.").Default("").Envar("TERRAFORM_NATIVE_PROVIDER_PATH").String() + pluginProcessTTL = app.Flag("provider-ttl", "TTL for the native plugin processes before they are replaced. Changing the default may increase memory consumption.").Default("100").Int() pollStateMetricInterval = app.Flag("poll-state-metric", "State metric recording interval").Default("5s").Duration() maxReconcileRate = app.Flag("max-reconcile-rate", "The global maximum rate per second at which resources may checked for drift from the desired state.").Default("10").Int() namespace = app.Flag("namespace", "Namespace used to set as default scope in default secret store config.").Default("crossplane-system").Envar("POD_NAMESPACE").String() @@ -152,24 +160,39 @@ func main() { ctx := context.Background() provider, err := config.GetProvider(ctx, false) kingpin.FatalIfError(err, "Cannot initialize the provider configuration") - setupCfg := clients.SetupConfig{ - ProviderVersion: providerVersion, - TerraformVersion: terraformVersion, - ProviderSource: providerSource, - TerraformProvider: provider.TerraformProvider, + ProviderVersion: ptr("2.2.0"), // providerVersion, + TerraformVersion: ptr("1.5.5"), // terraformVersion, + ProviderSource: ptr("equinix/equinix"), // providerSource, + TerraformProvider: provider.TerraformProvider, + NativeProviderPath: nativeProviderPath, } + + // if the native Terraform provider plugin's path is not configured via + // the env. variable TERRAFORM_NATIVE_PROVIDER_PATH or + // the `--terraform-native-provider-path` command-line option, + // we do not use the shared gRPC server and default to the regular + // Terraform CLI behaviour (of forking a plugin process per invocation). + // This removes some complexity for setting up development environments. + setupCfg.DefaultScheduler = terraform.NewNoOpProviderScheduler() + if len(*setupCfg.NativeProviderPath) != 0 { + setupCfg.DefaultScheduler = terraform.NewSharedProviderScheduler(log, *pluginProcessTTL, + terraform.WithSharedProviderOptions(terraform.WithNativeProviderPath(*setupCfg.NativeProviderPath), terraform.WithNativeProviderName("registry.terraform.io/"+*setupCfg.ProviderSource))) + } + + featureFlags := &feature.Flags{} o := upcontroller.Options{ Options: xpcontroller.Options{ Logger: log, GlobalRateLimiter: ratelimiter.NewGlobal(*maxReconcileRate), PollInterval: *pollInterval, MaxConcurrentReconciles: *maxReconcileRate, - Features: &feature.Flags{}, + Features: featureFlags, MetricOptions: &mo, }, Provider: provider, SetupFn: clients.TerraformSetupBuilder(setupCfg), + WorkspaceStore: terraform.NewWorkspaceStore(log, terraform.WithDisableInit(false), terraform.WithProcessReportInterval(*pollInterval), terraform.WithFeatures(featureFlags)), PollJitter: pollJitter, OperationTrackerStore: upcontroller.NewOperationStore(log), StartWebhooks: *certsDir != "", diff --git a/config/provider.go b/config/provider.go index 2225894..3b574fe 100644 --- a/config/provider.go +++ b/config/provider.go @@ -98,6 +98,16 @@ func GetProvider(_ context.Context, generationProvider bool) (*upconfig.Provider SkipOptCompLateInitialization(), LongProvision(), // TODO: use this only for Device and other long-provisioning resources ), + upconfig.WithBasePackages(upconfig.BasePackages{ + APIVersion: []string{ + // Default package for ProviderConfig APIs + "apis/v1beta1", + }, + Controller: []string{ + // Default package for ProviderConfig controllers + "internal/controller/providerconfig", + }, + }), ) for _, configure := range []func(provider *upconfig.Provider){ diff --git a/internal/clients/equinix.go b/internal/clients/equinix.go index 38e72df..86742e8 100644 --- a/internal/clients/equinix.go +++ b/internal/clients/equinix.go @@ -53,10 +53,12 @@ const ( ) type SetupConfig struct { - ProviderSource *string - ProviderVersion *string - TerraformVersion *string - TerraformProvider *schema.Provider + ProviderSource *string + ProviderVersion *string + TerraformVersion *string + TerraformProvider *schema.Provider + NativeProviderPath *string + DefaultScheduler terraform.ProviderScheduler } func prepareTerraformProviderConfiguration(creds map[string]string, pc v1beta1.ProviderConfiguration) map[string]any { @@ -94,6 +96,7 @@ func TerraformSetupBuilder(setupCfg SetupConfig) terraform.SetupFn { Source: *setupCfg.ProviderSource, Version: *setupCfg.ProviderVersion, }, + Scheduler: setupCfg.DefaultScheduler, } configRef := mg.GetProviderConfigReference() From 21e0d7db271cbd05ef42e1a320882007aed7ec47 Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Sun, 28 Jul 2024 19:33:29 -0400 Subject: [PATCH 6/7] debug: remove hardcoded TF setup values Signed-off-by: Marques Johansson --- cmd/provider/main.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/cmd/provider/main.go b/cmd/provider/main.go index 408eb48..a6cbf61 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -58,21 +58,16 @@ const ( tlsServerCertDir = "/tls/server" ) -func ptr[T any](s T) *T { return &s } - func main() { var ( - app = kingpin.New(filepath.Base(os.Args[0]), "Terraform based Crossplane provider for Equinix").DefaultEnvars() - debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() - syncInterval = app.Flag("sync", "Sync interval controls how often all resources will be double checked for drift.").Short('s').Default("1h").Duration() - pollInterval = app.Flag("poll", "Poll interval controls how often an individual resource should be checked for drift.").Default("10m").Duration() - leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").OverrideDefaultFromEnvar("LEADER_ELECTION").Bool() - // terraformVersion = app.Flag("terraform-version", "Terraform version.").Required().Envar("TERRAFORM_VERSION").String() - _ = app.Flag("terraform-version", "Terraform version.").Required().Envar("TERRAFORM_VERSION").String() - // providerSource = app.Flag("terraform-provider-source", "Terraform provider source.").Required().Envar("TERRAFORM_PROVIDER_SOURCE").String() - _ = app.Flag("terraform-provider-source", "Terraform provider source.").Required().Envar("TERRAFORM_PROVIDER_SOURCE").String() - // providerVersion = app.Flag("terraform-provider-version", "Terraform provider version.").Required().Envar("TERRAFORM_PROVIDER_VERSION").String() - _ = app.Flag("terraform-provider-version", "Terraform provider version.").Required().Envar("TERRAFORM_PROVIDER_VERSION").String() + app = kingpin.New(filepath.Base(os.Args[0]), "Terraform based Crossplane provider for Equinix").DefaultEnvars() + debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() + syncInterval = app.Flag("sync", "Sync interval controls how often all resources will be double checked for drift.").Short('s').Default("1h").Duration() + pollInterval = app.Flag("poll", "Poll interval controls how often an individual resource should be checked for drift.").Default("10m").Duration() + leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").OverrideDefaultFromEnvar("LEADER_ELECTION").Bool() + terraformVersion = app.Flag("terraform-version", "Terraform version.").Required().Envar("TERRAFORM_VERSION").String() + providerSource = app.Flag("terraform-provider-source", "Terraform provider source.").Required().Envar("TERRAFORM_PROVIDER_SOURCE").String() + providerVersion = app.Flag("terraform-provider-version", "Terraform provider version.").Required().Envar("TERRAFORM_PROVIDER_VERSION").String() nativeProviderPath = app.Flag("terraform-native-provider-path", "Terraform native provider path for shared execution.").Default("").Envar("TERRAFORM_NATIVE_PROVIDER_PATH").String() pluginProcessTTL = app.Flag("provider-ttl", "TTL for the native plugin processes before they are replaced. Changing the default may increase memory consumption.").Default("100").Int() pollStateMetricInterval = app.Flag("poll-state-metric", "State metric recording interval").Default("5s").Duration() @@ -161,9 +156,9 @@ func main() { provider, err := config.GetProvider(ctx, false) kingpin.FatalIfError(err, "Cannot initialize the provider configuration") setupCfg := clients.SetupConfig{ - ProviderVersion: ptr("2.2.0"), // providerVersion, - TerraformVersion: ptr("1.5.5"), // terraformVersion, - ProviderSource: ptr("equinix/equinix"), // providerSource, + ProviderVersion: providerVersion, + TerraformVersion: terraformVersion, + ProviderSource: providerSource, TerraformProvider: provider.TerraformProvider, NativeProviderPath: nativeProviderPath, } From 54dcb33a0a28df69c754af782140c698db7ce40e Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Sun, 28 Jul 2024 20:00:05 -0400 Subject: [PATCH 7/7] chore: regenerate providerconfigusages yaml Signed-off-by: Marques Johansson --- .../crds/equinix.jet.crossplane.io_providerconfigusages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/crds/equinix.jet.crossplane.io_providerconfigusages.yaml b/package/crds/equinix.jet.crossplane.io_providerconfigusages.yaml index 687f11f..38ee818 100644 --- a/package/crds/equinix.jet.crossplane.io_providerconfigusages.yaml +++ b/package/crds/equinix.jet.crossplane.io_providerconfigusages.yaml @@ -10,7 +10,7 @@ spec: names: categories: - crossplane - - providerconfig + - provider - equinix kind: ProviderConfigUsage listKind: ProviderConfigUsageList