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 3 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: 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
159 changes: 158 additions & 1 deletion pkg/config-api-provider/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@

import (
"context"
"os"

"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{}
)

var tokenURLDefault = "https://sso.common.cloud.hpe.com/as/token.oauth2"
1riatsila1 marked this conversation as resolved.
Show resolved Hide resolved

// 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 @@ -24,6 +35,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 @@ -39,14 +58,139 @@

// 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 77 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

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

Added line #L77 was not covered by tests
}

// If practitioner provided a configuration value for any of the
// attributes, it must be a known value.

if config.Host.IsUnknown() {
1riatsila1 marked this conversation as resolved.
Show resolved Hide resolved
resp.Diagnostics.AddAttributeError(
path.Root("host"),
"Unknown UXI API Host",
"The provider cannot create the UXI API client as there is an unknown configuration value for the UXI API host. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the UXI_HOST environment variable.",
)

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

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L84-L89

Added lines #L84 - L89 were not covered by tests
}

if config.ClientID.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("client_id"),
"Unknown Client ID",
"The provider cannot create the UXI API client as there is an unknown configuration value for the Client ID. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the CLIENT_ID environment variable.",
)

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

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L93-L98

Added lines #L93 - L98 were not covered by tests
}

if config.ClientSecret.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("client_secret"),
"Unknown Client Secret",
"The provider cannot create the UXI API client as there is an unknown configuration value for the Client Secret. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the CLIENT_SECRET environment variable.",
)

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

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L102-L107

Added lines #L102 - L107 were not covered by tests
}

if config.TokenURL.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("token_url"),
"Unknown Token URL",
"The provider cannot create the UXI API client as there is an unknown configuration value for the Token URL. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the TOKEN_URL environment variable.",
)

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

View check run for this annotation

Codecov / codecov/patch

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

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

if resp.Diagnostics.HasError() {
return

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L120 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 UXI API Host",
"The provider cannot create the UXI API client 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 154 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L148-L154

Added lines #L148 - L154 were not covered by tests
}

if clientID == "" {
resp.Diagnostics.AddAttributeError(
path.Root("client_id"),
"Missing Client ID",
"The provider cannot create the UXI API client 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 164 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L158-L164

Added lines #L158 - L164 were not covered by tests
}

if clientSecret == "" {
resp.Diagnostics.AddAttributeError(
path.Root("client_secret"),
"Missing Client Secret",
"The provider cannot create the UXI API client 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 174 in pkg/config-api-provider/provider/provider.go

View check run for this annotation

Codecov / codecov/patch

pkg/config-api-provider/provider/provider.go#L168-L174

Added lines #L168 - L174 were not covered by tests
}

if tokenURL == "" {
tokenURL = tokenURLDefault

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L178 was not covered by tests
}

if resp.Diagnostics.HasError() {
return

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L182 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)

resp.DataSourceData = uxiClient
resp.ResourceData = uxiClient
1riatsila1 marked this conversation as resolved.
Show resolved Hide resolved
}

// DataSources defines the data sources implemented in the provider.
Expand All @@ -69,3 +213,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 @@ -184,19 +210,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