From 10ca2b366bc00edf73cad3f29e1831f0b236f3bf Mon Sep 17 00:00:00 2001 From: Endre Karlson Date: Wed, 25 Sep 2024 11:26:10 +0200 Subject: [PATCH 1/4] Add support to use oauth credentials (#20) Signed-off-by: ekarlso Co-authored-by: ekarlso Signed-off-by: ekarlso --- config/databases/config.go | 49 ++++++++++++++++++++++++++- config/databases/config_test.go | 46 +++++++++++++++++++++++++ go.mod | 3 ++ go.sum | 4 +++ internal/clients/ovh.go | 59 +++++++++++++++++++++++++-------- 5 files changed, 147 insertions(+), 14 deletions(-) create mode 100644 config/databases/config_test.go diff --git a/config/databases/config.go b/config/databases/config.go index 0aa8624..0e98f76 100644 --- a/config/databases/config.go +++ b/config/databases/config.go @@ -1,15 +1,53 @@ package databases -import "github.com/crossplane/upjet/pkg/config" +import ( + "errors" + "fmt" + "reflect" + + "github.com/crossplane/upjet/pkg/config" +) const ( shortGroup = "databases" ) +var ErrTransformEndpointToConnection = errors.New("Error enriching Connection Details") + +func addConnectionInfo(attr map[string]any) (map[string][]byte, error) { + conn := map[string][]byte{} + + if endpoints, ok := attr["endpoints"]; ok { + fmt.Printf("Type is: %s\n", reflect.TypeOf(endpoints)) + + if endpointItems, ok := endpoints.([]map[string]interface{}); ok { + fmt.Printf("Decoded %+v", endpoints) + + for idx, endpoint := range endpointItems { + for key, value := range endpoint { + endpointKey := fmt.Sprintf("endpoint_%d_%s", idx, key) + + val := fmt.Sprintf("%s", value) + attr[endpointKey] = val + fmt.Printf("Endpoint %s -> %s", endpointKey, val) + } + } + } else { + return nil, fmt.Errorf("Could not decode endpoints: %w", ErrTransformEndpointToConnection) + } + } else { + return nil, fmt.Errorf("Could not find endpoints: %w", ErrTransformEndpointToConnection) + } + + return conn, nil +} + // Configure configures individual resources by adding custom ResourceConfigurators. func Configure(p *config.Provider) { p.AddResourceConfigurator("ovh_cloud_project_database", func(r *config.Resource) { r.ShortGroup = shortGroup + r.Sensitive.AdditionalConnectionDetailsFn = addConnectionInfo + r.UseAsync = true }) p.AddResourceConfigurator("ovh_cloud_project_database_database", func(r *config.Resource) { r.ShortGroup = shortGroup @@ -94,7 +132,16 @@ func Configure(p *config.Provider) { r.References["cluster_id"] = config.Reference{ TerraformName: "ovh_cloud_project_database", } + + r.Sensitive.AdditionalConnectionDetailsFn = func(attr map[string]any) (map[string][]byte, error) { + conn := map[string][]byte{} + + conn["username"] = []byte(attr["name"].(string)) + + return conn, nil + } }) + p.AddResourceConfigurator("ovh_cloud_project_database_user", func(r *config.Resource) { r.ShortGroup = shortGroup r.References["cluster_id"] = config.Reference{ diff --git a/config/databases/config_test.go b/config/databases/config_test.go new file mode 100644 index 0000000..d3468e9 --- /dev/null +++ b/config/databases/config_test.go @@ -0,0 +1,46 @@ +package databases + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAddEndpointInformation(t *testing.T) { + attrs := map[string]any{ + "endpoints": []map[string]any{ + { + "uri": "rediss://:@redis-526fe66a-od8a8be8e.database.cloud.ovh.net:20185", + "domain": "redis-526fe66a-od8a8be8e.database.cloud.ovh.net", + "port": 20185, + }, + }, + } + + conn, err := addConnectionInfo(attrs) + + assert.Nil(t, err) + assert.NotNil(t, conn) + + assert.Len(t, conn, 3) + assert.Equal(t, []byte("rediss://:@redis-526fe66a-od8a8be8e.database.cloud.ovh.net:20185"), conn["endpoint_0_uri"]) + assert.Equal(t, []byte("redis-526fe66a-od8a8be8e.database.cloud.ovh.net"), conn["endpoint_0_domain"]) + assert.Equal(t, []byte("20185"), conn["endpoint_0_port"]) +} + +func TestAddEndpointInformationFailedDecode(t *testing.T) { + attrs := map[string]any{ + "endpointss": []map[string]any{ + { + "uri": "rediss://:@redis-526fe66a-od8a8be8e.database.cloud.ovh.net:20185", + "domain": "redis-526fe66a-od8a8be8e.database.cloud.ovh.net", + "port": 20185, + }, + }, + } + + conn, err := addConnectionInfo(attrs) + + assert.NotNil(t, err) + assert.Nil(t, conn) +} diff --git a/go.mod b/go.mod index 0054bc6..323927c 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 github.com/gobuffalo/flect v1.0.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -83,6 +84,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/muvaf/typewriter v0.0.0-20220131201631-921e94e8e8d7 // indirect github.com/oklog/run v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect @@ -90,6 +92,7 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/tmccombs/hcl2json v0.3.3 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect diff --git a/go.sum b/go.sum index ae1bc31..ee088fb 100644 --- a/go.sum +++ b/go.sum @@ -65,6 +65,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4 github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -236,6 +238,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tmccombs/hcl2json v0.3.3 h1:+DLNYqpWE0CsOQiEZu+OZm5ZBImake3wtITYxQ8uLFQ= github.com/tmccombs/hcl2json v0.3.3/go.mod h1:Y2chtz2x9bAeRTvSibVRVgbLJhLJXKlUeIvjeVdnm4w= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/internal/clients/ovh.go b/internal/clients/ovh.go index 632b5df..57797e4 100644 --- a/internal/clients/ovh.go +++ b/internal/clients/ovh.go @@ -25,8 +25,18 @@ const ( errTrackUsage = "cannot track ProviderConfig usage" errExtractCredentials = "cannot extract credentials" errUnmarshalCredentials = "cannot unmarshal ovh credentials as JSON" + errNoValidCredentials = "cannot find valid credentials" ) +type OVHCredentials struct { + Endpoint string `json:"endpoint"` + ApplicationKey string `json:"application_key,omitempty"` + ApplicationSecret string `json:"application_secret,omitempty"` + ConsumerKey string `json:"consumer_key,omitempty"` + ClientID string `json:"client_id,omitempty"` + ClientSecret string `json:"client_secret,omitempty"` +} + // TerraformSetupBuilder builds Terraform a terraform.SetupFn function which // returns Terraform provider setup configuration func TerraformSetupBuilder(version, providerSource, providerVersion string) terraform.SetupFn { @@ -53,22 +63,45 @@ func TerraformSetupBuilder(version, providerSource, providerVersion string) terr return ps, errors.Wrap(err, errTrackUsage) } - data, err := resource.CommonCredentialExtractor(ctx, pc.Spec.Credentials.Source, client, pc.Spec.Credentials.CommonCredentialSelectors) + cfg, err := configureClient(ctx, pc, client) if err != nil { - return ps, errors.Wrap(err, errExtractCredentials) - } - creds := map[string]string{} - if err := json.Unmarshal(data, &creds); err != nil { - return ps, errors.Wrap(err, errUnmarshalCredentials) + return ps, err } - // Set credentials in Terraform provider configuration. - ps.Configuration = map[string]any{ - "endpoint": creds["endpoint"], - "application_key": creds["application_key"], - "application_secret": creds["application_secret"], - "consumer_key": creds["consumer_key"], - } + ps.Configuration = cfg + return ps, nil } } + +func configureClient(ctx context.Context, pc *v1beta1.ProviderConfig, client client.Client) (map[string]any, error) { + data, err := resource.CommonCredentialExtractor(ctx, pc.Spec.Credentials.Source, client, pc.Spec.Credentials.CommonCredentialSelectors) + if err != nil { + return nil, errors.Wrap(err, errExtractCredentials) + } + + var creds OVHCredentials + if err := json.Unmarshal(data, &creds); err != nil { + return nil, errors.Wrap(err, errUnmarshalCredentials) + } + + // Set credentials in Terraform provider configuration. + cfg := map[string]any{ + "endpoint": creds.Endpoint, + } + + if creds.ApplicationKey != "" && creds.ApplicationSecret != "" && creds.ConsumerKey != "" { + cfg["application_key"] = creds.ApplicationKey + cfg["application_secret"] = creds.ApplicationSecret + cfg["consumer_key"] = creds.ConsumerKey + return cfg, nil + } + + if creds.ClientID != "" && creds.ClientSecret != "" { + cfg["client_id"] = creds.ClientID + cfg["client_secret"] = creds.ClientSecret + return cfg, nil + } + + return cfg, errors.New(errNoValidCredentials) +} From 9918d1b7bda5ad9412c80a3fd396f885a3bec836 Mon Sep 17 00:00:00 2001 From: ekarlso Date: Tue, 8 Oct 2024 21:25:37 +0200 Subject: [PATCH 2/4] Attempt to fix tests --- config/databases/config.go | 20 +++++++++----------- config/databases/config_test.go | 15 ++++++++------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/config/databases/config.go b/config/databases/config.go index 0e98f76..43b14af 100644 --- a/config/databases/config.go +++ b/config/databases/config.go @@ -3,7 +3,6 @@ package databases import ( "errors" "fmt" - "reflect" "github.com/crossplane/upjet/pkg/config" ) @@ -18,18 +17,17 @@ func addConnectionInfo(attr map[string]any) (map[string][]byte, error) { conn := map[string][]byte{} if endpoints, ok := attr["endpoints"]; ok { - fmt.Printf("Type is: %s\n", reflect.TypeOf(endpoints)) + if endpointList, ok := endpoints.([]any); ok { + for idx, item := range endpointList { + if endpoint, ok := item.(map[string]any); ok { + for key, value := range endpoint { + endpointKey := fmt.Sprintf("endpoint_%d_%s", idx, key) - if endpointItems, ok := endpoints.([]map[string]interface{}); ok { - fmt.Printf("Decoded %+v", endpoints) + val := fmt.Sprintf("%s", value) + attr[endpointKey] = val - for idx, endpoint := range endpointItems { - for key, value := range endpoint { - endpointKey := fmt.Sprintf("endpoint_%d_%s", idx, key) - - val := fmt.Sprintf("%s", value) - attr[endpointKey] = val - fmt.Printf("Endpoint %s -> %s", endpointKey, val) + fmt.Printf("Endpoint %s -> %s\n", endpointKey, val) + } } } } else { diff --git a/config/databases/config_test.go b/config/databases/config_test.go index d3468e9..ade6b07 100644 --- a/config/databases/config_test.go +++ b/config/databases/config_test.go @@ -7,15 +7,16 @@ import ( ) func TestAddEndpointInformation(t *testing.T) { - attrs := map[string]any{ - "endpoints": []map[string]any{ - { - "uri": "rediss://:@redis-526fe66a-od8a8be8e.database.cloud.ovh.net:20185", - "domain": "redis-526fe66a-od8a8be8e.database.cloud.ovh.net", - "port": 20185, - }, + endpoints := []map[string]any{ + { + "uri": "rediss://:@redis-526fe66a-od8a8be8e.database.cloud.ovh.net:20185", + "domain": "redis-526fe66a-od8a8be8e.database.cloud.ovh.net", + "port": 20185, }, } + attrs := map[string]any{ + "endpoints": endpoints, + } conn, err := addConnectionInfo(attrs) From e693ae4054c270f6013ed0eecd31025b15911572 Mon Sep 17 00:00:00 2001 From: ekarlso Date: Thu, 10 Oct 2024 10:00:26 +0200 Subject: [PATCH 3/4] Fix casts --- config/databases/config.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/config/databases/config.go b/config/databases/config.go index 43b14af..f9dbeea 100644 --- a/config/databases/config.go +++ b/config/databases/config.go @@ -23,10 +23,19 @@ func addConnectionInfo(attr map[string]any) (map[string][]byte, error) { for key, value := range endpoint { endpointKey := fmt.Sprintf("endpoint_%d_%s", idx, key) - val := fmt.Sprintf("%s", value) - attr[endpointKey] = val + var val []byte + switch value.(type) { + case float64: + val = []byte(fmt.Sprintf("%.0f", value)) + case bool: + val = []byte(fmt.Sprintf("%b", value)) + case string: + val = []byte(fmt.Sprintf("%s", value)) + default: + continue + } - fmt.Printf("Endpoint %s -> %s\n", endpointKey, val) + conn[endpointKey] = val } } } From 4939347ac560d1a558582c1cc9dacab886ef70c6 Mon Sep 17 00:00:00 2001 From: ekarlso Date: Thu, 10 Oct 2024 10:08:39 +0200 Subject: [PATCH 4/4] Use require --- config/databases/config_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config/databases/config_test.go b/config/databases/config_test.go index ade6b07..aa307c3 100644 --- a/config/databases/config_test.go +++ b/config/databases/config_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestAddEndpointInformation(t *testing.T) { @@ -20,7 +21,7 @@ func TestAddEndpointInformation(t *testing.T) { conn, err := addConnectionInfo(attrs) - assert.Nil(t, err) + require.NoError(t, err) assert.NotNil(t, conn) assert.Len(t, conn, 3) @@ -42,6 +43,6 @@ func TestAddEndpointInformationFailedDecode(t *testing.T) { conn, err := addConnectionInfo(attrs) - assert.NotNil(t, err) + require.Error(t, err) assert.Nil(t, conn) }