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

feat: integrate client into TF provider #18

Merged
merged 8 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ fmt:
clean:
find . -name ".coverage*" -type f -delete

plan:
plan +ARGS='':
cd {{ CONFIG_API_PROVIDER_DIR }} && go install .
cd {{ CONFIG_API_PROVIDER_DIR }}/examples/full-demo && terraform plan
cd {{ CONFIG_API_PROVIDER_DIR }}/examples/full-demo && terraform plan -var client_secret=secret {{ ARGS }}
1riatsila1 marked this conversation as resolved.
Show resolved Hide resolved

apply:
apply +ARGS='':
cd {{ CONFIG_API_PROVIDER_DIR }} && go install .
cd {{ CONFIG_API_PROVIDER_DIR }}/examples/full-demo && terraform apply
cd {{ CONFIG_API_PROVIDER_DIR }}/examples/full-demo && terraform apply -var client_secret=secret {{ ARGS }}
7 changes: 6 additions & 1 deletion pkg/config-api-provider/examples/full-demo/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ terraform {
}
}

provider "uxi" {}
provider "uxi" {
host = "test.api.capenetworks.com"
client_id = "client_id"
client_secret = var.client_secret
token_url = "https://test.sso.common.cloud.hpe.com/as/token.oauth2"
}

data "uxi_root_group" "my_root_group" {}

Expand Down
1 change: 1 addition & 0 deletions pkg/config-api-provider/examples/full-demo/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
variable "client_secret" {}
8 changes: 3 additions & 5 deletions pkg/config-api-provider/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@ toolchain go1.22.5

require (
github.com/aruba-uxi/configuration-api-terraform-provider/pkg/config-api-client v0.0.0-00010101000000-000000000000
github.com/h2non/gock v1.2.0
github.com/hashicorp/terraform-plugin-framework v1.9.0
github.com/hashicorp/terraform-plugin-go v0.23.0
github.com/hashicorp/terraform-plugin-testing v1.10.0
github.com/stretchr/testify v1.9.0
golang.org/x/oauth2 v0.17.0
)

require (
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
github.com/agext/levenshtein v1.2.2 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
Expand Down Expand Up @@ -49,8 +50,6 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
Expand All @@ -66,7 +65,6 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.34.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/aruba-uxi/configuration-api-terraform-provider/pkg/config-api-client => ../config-api-client
10 changes: 8 additions & 2 deletions pkg/config-api-provider/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
Expand Down Expand Up @@ -120,6 +124,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
Expand All @@ -131,8 +137,6 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
Expand Down Expand Up @@ -163,6 +167,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
117 changes: 116 additions & 1 deletion pkg/config-api-provider/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,31 @@

import (
"context"
"os"

datasources "github.com/aruba-uxi/configuration-api-terraform-provider/pkg/terraform-provider-configuration/provider/data-sources"
"github.com/aruba-uxi/configuration-api-terraform-provider/pkg/terraform-provider-configuration/provider/resources"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"

"github.com/aruba-uxi/configuration-api-terraform-provider/pkg/config-api-client"
"github.com/hashicorp/terraform-plugin-framework/types"
"net/http"

"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ provider.Provider = &uxiConfigurationProvider{}
)

const tokenURLDefault = "https://sso.common.cloud.hpe.com/as/token.oauth2"

// New is a helper function to simplify provider server and testing implementation.
func New(version string) func() provider.Provider {
return func() provider.Provider {
Expand All @@ -25,6 +36,14 @@
}
}

// maps provider schema data to a Go type.
type uxiProviderModel struct {
Host types.String `tfsdk:"host"`
ClientID types.String `tfsdk:"client_id"`
ClientSecret types.String `tfsdk:"client_secret"`
1riatsila1 marked this conversation as resolved.
Show resolved Hide resolved
TokenURL types.String `tfsdk:"token_url"`
}

type uxiConfigurationProvider struct {
// version is set to the provider version on release, "dev" when the
// provider is built and ran locally, and "test" when running acceptance
Expand All @@ -40,14 +59,97 @@

// Schema defines the provider-level schema for configuration data.
func (p *uxiConfigurationProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{}
resp.Schema = schema.Schema{Attributes: map[string]schema.Attribute{
"host": schema.StringAttribute{Optional: true},
"client_id": schema.StringAttribute{Optional: true},
"client_secret": schema.StringAttribute{Optional: true, Sensitive: true},
"token_url": schema.StringAttribute{Optional: true},
1riatsila1 marked this conversation as resolved.
Show resolved Hide resolved
}}
}

// TODO: Obtain a greenlake access token
// Configure prepares a Configuration API client for data sources and resources.
func (p *uxiConfigurationProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
// Init
var config uxiProviderModel
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return

Check warning on line 78 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L78

Added line #L78 was not covered by tests
}

host := os.Getenv("UXI_HOST")
clientID := os.Getenv("CLIENT_ID")
clientSecret := os.Getenv("CLIENT_SECRET")
tokenURL := os.Getenv("TOKEN_URL")

if !config.Host.IsNull() {
host = config.Host.ValueString()
}

if !config.ClientID.IsNull() {
clientID = config.ClientID.ValueString()
}

if !config.ClientSecret.IsNull() {
clientSecret = config.ClientSecret.ValueString()
}

if !config.TokenURL.IsNull() {
tokenURL = config.TokenURL.ValueString()
}

// If any of the expected configurations are missing, return
// errors with provider-specific guidance.

if host == "" {
resp.Diagnostics.AddAttributeError(
path.Root("host"),
"Missing Host",
"The provider cannot initialize as there is a missing or empty value for the UXI API host. "+
"Set the host value in the configuration or use the UXI_HOST environment variable. "+
"If either is already set, ensure the value is not empty.",
)

Check warning on line 112 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L106-L112

Added lines #L106 - L112 were not covered by tests
}

if clientID == "" {
resp.Diagnostics.AddAttributeError(
path.Root("client_id"),
"Missing Client ID",
"The provider cannot initialize as there is a missing or empty value for the Client ID. "+
"Set the Client ID value in the configuration or use the CLIENT_ID environment variable. "+
"If either is already set, ensure the value is not empty.",
)

Check warning on line 122 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L116-L122

Added lines #L116 - L122 were not covered by tests
}

if clientSecret == "" {
resp.Diagnostics.AddAttributeError(
path.Root("client_secret"),
"Missing Client Secret",
"The provider cannot initialize as there is a missing or empty value for the Client Secret. "+
"Set the Client Secret value in the configuration or use the CLIENT_SECRET environment variable. "+
"If either is already set, ensure the value is not empty.",
)

Check warning on line 132 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L126-L132

Added lines #L126 - L132 were not covered by tests
}

if tokenURL == "" {
tokenURL = tokenURLDefault

Check warning on line 136 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L136

Added line #L136 was not covered by tests
}

if resp.Diagnostics.HasError() {
return

Check warning on line 140 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L140

Added line #L140 was not covered by tests
}

// initialise client
uxiConfiguration := config_api_client.NewConfiguration()
uxiConfiguration.Host = host
uxiConfiguration.Scheme = "https"
uxiConfiguration.HTTPClient = getHttpClient(clientID, clientSecret, tokenURL)
uxiClient := config_api_client.NewAPIClient(uxiConfiguration)

// Make the client available during DataSource and Resource type Configure methods.
resp.DataSourceData = uxiClient
resp.ResourceData = uxiClient
}

// DataSources defines the data sources implemented in the provider.
Expand All @@ -72,3 +174,16 @@
resources.NewServiceTestGroupAssignmentResource,
}
}

func getHttpClient(clientID string, clientSecret string, tokenURL string) *http.Client {
// Set up the client credentials config
config := &clientcredentials.Config{
ClientID: clientID,
ClientSecret: clientSecret,
TokenURL: tokenURL,
AuthStyle: oauth2.AuthStyleInParams,
}

// Create a context and fetch a tokencould
return config.Client(context.Background())
}
65 changes: 39 additions & 26 deletions pkg/config-api-provider/provider/resources/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import (
"context"
"fmt"

"github.com/aruba-uxi/configuration-api-terraform-provider/pkg/config-api-client"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
Expand All @@ -24,10 +27,10 @@
}

type GroupResponseModel struct {
UID string
Name string
ParentUid *string
Path string
UID string `json:"uid"`
Name string `json:"name"`
ParentUid *string `json:"parent_uid"`
Path string `json:"path"`
}

type GroupCreateRequestModel struct {
Expand All @@ -43,7 +46,9 @@
return &groupResource{}
}

type groupResource struct{}
type groupResource struct {
client *config_api_client.APIClient
}

func (r *groupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_group"
Expand Down Expand Up @@ -73,6 +78,24 @@
}

func (r *groupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Add a nil check when handling ProviderData because Terraform
// sets that data after it calls the ConfigureProvider RPC.
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*config_api_client.APIClient)

if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *config_api_client.APIClient, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

Check warning on line 93 in pkg/config-api-provider/provider/resources/group.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/resources/group.go#L90-L93

Added lines #L90 - L93 were not covered by tests
1riatsila1 marked this conversation as resolved.
Show resolved Hide resolved

return

Check warning on line 95 in pkg/config-api-provider/provider/resources/group.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/resources/group.go#L95

Added line #L95 was not covered by tests
}

r.client = client
}

func (r *groupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
Expand All @@ -84,17 +107,20 @@
return
}

// TODO: Call client create-group method
// We are mocking the response of the client for this early stage of development
group := CreateGroup(GroupCreateRequestModel{
Name: plan.Name.ValueString(),
ParentUid: plan.ParentGroupId.ValueString(),
})
groups_post_request := config_api_client.NewGroupsPostRequest(plan.ParentGroupId.ValueString(), plan.Name.ValueString())
group, _, err := r.client.ConfigurationAPI.GroupsPostConfigurationAppV1GroupsPost(context.Background()).GroupsPostRequest(*groups_post_request).Execute()
if err != nil {
resp.Diagnostics.AddError(
"Error creating group",
"Could not create group, unexpected error: "+err.Error(),
)
return

Check warning on line 117 in pkg/config-api-provider/provider/resources/group.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/resources/group.go#L113-L117

Added lines #L113 - L117 were not covered by tests
}

// Update the state to match the plan (replace with response from client)
plan.ID = types.StringValue(group.UID)
plan.ID = types.StringValue(group.Uid)
plan.Name = types.StringValue(group.Name)
plan.ParentGroupId = types.StringValue(*group.ParentUid)
plan.ParentGroupId = types.StringValue(group.ParentUid)

// Set state to fully populated data
diags = resp.State.Set(ctx, plan)
Expand Down Expand Up @@ -187,19 +213,6 @@
}
}

var CreateGroup = func(request GroupCreateRequestModel) GroupResponseModel {
// TODO: Query the group using the client

parent_uid := "mock_parent_uid"

return GroupResponseModel{
UID: "mock_uid",
Name: "mock_name",
ParentUid: &parent_uid,
Path: "mock_path",
}
}

var UpdateGroup = func(request GroupUpdateRequestModel) GroupResponseModel {
// TODO: Query the group using the client

Expand Down
Loading
Loading