Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix external name #60

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions apis/zz_register.go

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

48 changes: 4 additions & 44 deletions cluster/images/provider-jet-equinix/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,54 +1,14 @@
FROM BASEIMAGE
FROM alpine:3.19.1
RUN apk --no-cache add ca-certificates bash

ARG TARGETOS
ARG TARGETARCH
ARG TINI_VERSION
ENV USER_ID=1001

# Setup Terraform environment
ADD "bin/${TARGETOS}_${TARGETARCH}/provider" /usr/local/bin/provider

## Provider-dependent configuration
ARG CROSSPLANE_PROVIDER_VERSION
ARG TERRAFORM_VERSION
ARG TERRAFORM_PROVIDER_SOURCE
ARG TERRAFORM_PROVIDER_VERSION
ARG TERRAFORM_PROVIDER_DOWNLOAD_NAME
ARG TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX
ARG TERRAFORM_NATIVE_PROVIDER_BINARY
## End of - Provider-dependent configuration

ENV PLUGIN_DIR /terraform/provider-mirror/registry.terraform.io/${TERRAFORM_PROVIDER_SOURCE}/${TERRAFORM_PROVIDER_VERSION}/${TARGETOS}_${TARGETARCH}
ENV TF_CLI_CONFIG_FILE /terraform/.terraformrc
ENV TF_FORK 0

RUN mkdir -p ${PLUGIN_DIR}

ADD https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_${TARGETOS}_${TARGETARCH}.zip /tmp
ADD ${TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX}/${TERRAFORM_PROVIDER_DOWNLOAD_NAME}_${TERRAFORM_PROVIDER_VERSION}_${TARGETOS}_${TARGETARCH}.zip /tmp

ADD terraformrc.hcl ${TF_CLI_CONFIG_FILE}

RUN unzip /tmp/terraform_${TERRAFORM_VERSION}_${TARGETOS}_${TARGETARCH}.zip -d /usr/local/bin \
&& chmod +x /usr/local/bin/terraform \
&& rm -f /tmp/terraform_${TERRAFORM_VERSION}_${TARGETOS}_${TARGETARCH}.zip \
&& unzip /tmp/${TERRAFORM_PROVIDER_DOWNLOAD_NAME}_${TERRAFORM_PROVIDER_VERSION}_${TARGETOS}_${TARGETARCH}.zip -d /tmp/ \
&& mv /tmp/${TERRAFORM_NATIVE_PROVIDER_BINARY} ${PLUGIN_DIR} \
&& chmod +x ${PLUGIN_DIR}/${TERRAFORM_NATIVE_PROVIDER_BINARY} \
&& rm -f /tmp/${TERRAFORM_PROVIDER_DOWNLOAD_NAME}_${TERRAFORM_PROVIDER_VERSION}_${TARGETOS}_${TARGETARCH}.zip \
&& chown -R ${USER_ID}:${USER_ID} /terraform
# End of - Setup Terraform environment

ADD bin/$TARGETOS\_$TARGETARCH/provider /usr/local/bin/crossplane-equinix-provider

# Provider controller needs these environment variable at runtime
ENV TERRAFORM_VERSION ${TERRAFORM_VERSION}
ENV TERRAFORM_PROVIDER_SOURCE ${TERRAFORM_PROVIDER_SOURCE}
ENV TERRAFORM_PROVIDER_VERSION ${TERRAFORM_PROVIDER_VERSION}
ENV TERRAFORM_NATIVE_PROVIDER_PATH ${PLUGIN_DIR}/${TERRAFORM_NATIVE_PROVIDER_BINARY}
ENV TF_APPEND_USER_AGENT crossplane-provider-jet-equinix/${CROSSPLANE_PROVIDER_VERSION}
ENV USER_ID=65532

USER ${USER_ID}
EXPOSE 8080

ENTRYPOINT ["crossplane-equinix-provider"]
ENTRYPOINT ["provider"]
30 changes: 1 addition & 29 deletions cluster/images/provider-jet-equinix/Makefile
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
# ====================================================================================
# Setup Project

PLATFORMS := linux_amd64 linux_arm64
include ../../../build/makelib/common.mk

# ====================================================================================
# Options
IMAGE = $(BUILD_REGISTRY)/provider-jet-equinix-controller-$(ARCH)

include ../../../build/makelib/imagelight.mk

# OSBASEIMAGE = scratch
# OSBASEIMAGE = gcr.io/distroless/static@sha256:1f580b0a1922c3e54ae15b0758b5747b260bd99d39d40c2edb3e7f6e2452298b

ifeq ($(origin OSBASEIMAGE),undefined)
OSBASE ?= alpine:3.13
ifeq ($(TARGETARCH),$(filter $(TARGETARCH),amd64 ppc64le))
OSBASEIMAGE = $(OSBASE)
else ifeq ($(TARGETARCH),arm64)
OSBASEIMAGE = arm64v8/$(OSBASE)
else
$(error unsupported architecture $(TARGETARCH))
endif
endif
# ====================================================================================
# Targets

Expand All @@ -36,26 +22,12 @@ img.publish:
@$(OK) Image publish skipped for $(IMAGE)

img.build.shared:
@$(INFO) docker build $(IMAGE)
@cp Dockerfile $(IMAGE_TEMP_DIR) || $(FAIL)
@cp terraformrc.hcl $(IMAGE_TEMP_DIR) || $(FAIL)
@cp $(OUTPUT_DIR)/bin/$(OS)_$(ARCH)/provider $(IMAGE_TEMP_DIR) || $(FAIL)
@cp -r $(OUTPUT_DIR)/bin/ $(IMAGE_TEMP_DIR)/bin || $(FAIL)
@cd $(IMAGE_TEMP_DIR) && $(SED_CMD) 's|BASEIMAGE|$(OSBASEIMAGE)|g' Dockerfile || $(FAIL)
@docker buildx build $(BUILD_ARGS) \
--platform $(IMAGE_PLATFORMS) \
--build-arg ARCH=$(ARCH) \
--build-arg TINI_VERSION=$(TINI_VERSION) \
--build-arg TERRAFORM_VERSION=$(TERRAFORM_VERSION) \
--build-arg TERRAFORM_PROVIDER_SOURCE=$(TERRAFORM_PROVIDER_SOURCE) \
--build-arg TERRAFORM_PROVIDER_VERSION=$(TERRAFORM_PROVIDER_VERSION) \
--build-arg TERRAFORM_PROVIDER_DOWNLOAD_NAME=$(TERRAFORM_PROVIDER_DOWNLOAD_NAME) \
--build-arg TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX=$(TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX) \
--build-arg TERRAFORM_NATIVE_PROVIDER_BINARY=$(TERRAFORM_NATIVE_PROVIDER_BINARY) \
--build-arg CROSSPLANE_PROVIDER_VERSION=$(VERSION) \
-t $(IMAGE) \
$(IMAGE_TEMP_DIR) || $(FAIL)
@$(OK) docker build $(IMAGE)

img.promote:
@$(INFO) Skipping image promotion from $(FROM_IMAGE) to $(TO_IMAGE)
Expand Down
9 changes: 0 additions & 9 deletions cluster/images/provider-jet-equinix/terraformrc.hcl

This file was deleted.

47 changes: 26 additions & 21 deletions cmd/provider/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ package main

import (
"context"
"fmt"
"os"
"path/filepath"
"runtime/debug"
"time"

xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
Expand Down Expand Up @@ -65,11 +67,11 @@ func main() {
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()
_ = app.Flag("terraform-version", "Terraform version.").Required().Envar("TERRAFORM_VERSION").String()
_ = app.Flag("terraform-provider-source", "Terraform provider source.").Required().Envar("TERRAFORM_PROVIDER_SOURCE").String()
_ = app.Flag("terraform-provider-version", "Terraform provider version.").Required().Envar("TERRAFORM_PROVIDER_VERSION").String()
_ = app.Flag("terraform-native-provider-path", "Terraform native provider path for shared execution.").Default("").Envar("TERRAFORM_NATIVE_PROVIDER_PATH").String()
_ = app.Flag("provider-ttl", "DEPRECATED: 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()
Expand Down Expand Up @@ -156,24 +158,11 @@ func main() {
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,
NativeProviderPath: nativeProviderPath,
ProviderVersion: providerVersion("github.com/equinix/terraform-provider-equinix"),
TerraformProvider: provider.TerraformProvider,
}

// 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{
Expand All @@ -187,7 +176,6 @@ func main() {
},
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 != "",
Expand Down Expand Up @@ -231,3 +219,20 @@ func main() {
kingpin.FatalIfError(controller.Setup(mgr, o), "Cannot setup Equinix controllers")
kingpin.FatalIfError(mgr.Start(ctrl.SetupSignalHandler()), "Cannot start controller manager")
}

func providerVersion(moduleName string) string {
info, ok := debug.ReadBuildInfo()
if !ok {
fmt.Println("No build info available")
return ""
}

for _, dep := range info.Deps {
if dep.Path == moduleName {
return dep.Version
}
}

// Return empty string if module is not found
return ""
}
32 changes: 32 additions & 0 deletions config/metal/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2024 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: CC0-1.0
package metal

import (
"context"
"errors"
"fmt"

"github.com/crossplane/upjet/pkg/config"
)

// Configure adds configurations for the metal group.
func Configure(p *config.Provider) {
p.AddResourceConfigurator("equinix_metal_port_vlan_attachment", func(r *config.Resource) {
// ID assigned in Terraform is PortID + ":" + VlanID
r.ExternalName = config.IdentifierFromProvider
r.ExternalName.IdentifierFields = []string{"port_id", "vlan_id"}
r.ExternalName.GetIDFn = func(ctx context.Context, externalName string, parameters map[string]interface{}, cfg map[string]interface{}) (string, error) {
portID, ok := parameters["port_id"]
if !ok || portID.(string) == "" {
return "", errors.New("port_id cannot be empty")
}
vlanID, ok := parameters["vlan_id"]
if !ok || vlanID.(string) == "" {
return "", errors.New("vlan_id cannot be empty")
}
return fmt.Sprintf("%s:%s", portID.(string), vlanID.(string)), nil
}
})
}
29 changes: 27 additions & 2 deletions config/overrides.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,44 @@ package config

import (
// Note(turkenh): we are importing this to embed provider schema document

"context"
_ "embed"
"errors"
"strings"

upconfig "github.com/crossplane/upjet/pkg/config"
)

var invalidNonEmptyString = "badc0de1-a15d-401b-94e4-deadbeefc0de" // "invalidnonemptystring"

// IdentifierAssignedByEquinix will work for all Equinix types because even if
// the ID is assigned by user, we'll see it in the TF State ID. The
// resource-specific configurations should override this whenever possible.
// See https://github.com/crossplane/upjet/blob/main/docs/configuring-a-resource.md#case-2-identifier-from-provider for more details.
func IdentifierAssignedByEquinix() upconfig.ResourceOption {
return func(r *upconfig.Resource) {
r.ExternalName = upconfig.IdentifierFromProvider
e := upconfig.IdentifierFromProvider
e.GetIDFn = func(ctx context.Context, externalName string, parameters map[string]interface{}, cfg map[string]interface{}) (string, error) {
if externalName == "" {
return invalidNonEmptyString, nil
}
return externalName, nil
}
e.GetExternalNameFn = func(tfstate map[string]any) (string, error) {
id, ok := tfstate["id"]
if !ok {
return "", errors.New("id attribute missing from state file")
}

idStr, ok := id.(string)
if !ok {
return "", errors.New("value of id needs to be string")
}

return idStr, nil
}
r.ExternalName = e
}
}

Expand Down Expand Up @@ -98,7 +123,7 @@ func SkipOptCompLateInitialization() upconfig.ResourceOption {
return func(r *upconfig.Resource) {
for k, s := range r.TerraformResource.Schema {
if s.Computed && s.Optional {
for _, conflict := range s.ConflictsWith {
for _, conflict := range append(s.ConflictsWith, s.ExactlyOneOf...) {
if cs := r.TerraformResource.Schema[conflict]; cs.Computed && cs.Optional {
r.LateInitializer.AddIgnoredCanonicalFields(conflict)
r.LateInitializer.AddIgnoredCanonicalFields(k)
Expand Down
Loading
Loading