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

[Manual Backport] Patchback/backports/stable 1/48f2caff9a2b201248a0725c5e406d6f60d06329/pr 30 #48

Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions changelogs/fragments/20240619-aap-versioning.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
minor_changes:
- Support dynamic value for AAP endpoints since the value depends on the AAP version (https://github.com/ansible/terraform-provider-aap/pull/30).
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/hashicorp/terraform-plugin-framework-jsontypes v0.1.0
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
github.com/hashicorp/terraform-plugin-go v0.19.1
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.6.0
github.com/stretchr/testify v1.8.4
)
Expand Down Expand Up @@ -45,7 +46,6 @@ require (
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.21.0 // indirect
github.com/hashicorp/terraform-json v0.22.1 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
Expand Down
16 changes: 0 additions & 16 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -86,28 +86,18 @@ github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly8
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hc-install v0.6.4 h1:QLqlM56/+SIIGvGcfFiwMY3z5WGXT066suo/v9Km8e0=
github.com/hashicorp/hc-install v0.6.4/go.mod h1:05LWLy8TD842OtgcfBbOT0WMoInBMUSHjmDx10zuBIA=
github.com/hashicorp/hc-install v0.7.0 h1:Uu9edVqjKQxxuD28mR5TikkKDd/p55S8vzPC1659aBk=
github.com/hashicorp/hc-install v0.7.0/go.mod h1:ELmmzZlGnEcqoUMKUuykHaPCIR1sYLYX+KSggWSKZuA=
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo=
github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw=
github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U=
github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk=
github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec=
github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A=
github.com/hashicorp/terraform-plugin-docs v0.19.2 h1:YjdKa1vuqt9EnPYkkrv9HnGZz175HhSJ7Vsn8yZeWus=
github.com/hashicorp/terraform-plugin-docs v0.19.2/go.mod h1:gad2aP6uObFKhgNE8DR9nsEuEQnibp7il0jZYYOunWY=
github.com/hashicorp/terraform-plugin-docs v0.19.4 h1:G3Bgo7J22OMtegIgn8Cd/CaSeyEljqjH3G39w28JK4c=
github.com/hashicorp/terraform-plugin-docs v0.19.4/go.mod h1:4pLASsatTmRynVzsjEhbXZ6s7xBlUw/2Kt0zfrq8HxA=
github.com/hashicorp/terraform-plugin-framework v1.4.2 h1:P7a7VP1GZbjc4rv921Xy5OckzhoiO3ig6SGxwelD2sI=
Expand Down Expand Up @@ -228,8 +218,6 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U=
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand All @@ -238,8 +226,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -270,8 +256,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
75 changes: 69 additions & 6 deletions internal/provider/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package provider
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
Expand All @@ -18,18 +20,64 @@ type ProviderHTTPClient interface {
Get(path string) ([]byte, diag.Diagnostics)
Update(path string, data io.Reader) ([]byte, diag.Diagnostics)
Delete(path string) ([]byte, diag.Diagnostics)
setApiEndpoint() diag.Diagnostics
getApiEndpoint() string
}

type AAPApiEndpointResponse struct {
Apis struct {
Controller string `json:"controller"`
} `json:"apis"`
CurrentVersion string `json:"current_version"`
}

func readApiEndpoint(client ProviderHTTPClient) (string, diag.Diagnostics) {
body, diags := client.Get("/api/")
if diags.HasError() {
return "", diags
}
var response AAPApiEndpointResponse
err := json.Unmarshal(body, &response)
if err != nil {
diags.AddError(
fmt.Sprintf("Unable to parse AAP API endpoint response: %s", string(body)),
fmt.Sprintf("Unexpected error: %s", err.Error()),
)
return "", diags
}
if len(response.Apis.Controller) > 0 {
body, diags = client.Get(response.Apis.Controller)
if diags.HasError() {
return "", diags
}
// Parse response
err = json.Unmarshal(body, &response)
if err != nil {
diags.AddError(
fmt.Sprintf("Unable to parse AAP API endpoint response: %s", string(body)),
fmt.Sprintf("Unexpected error: %s", err.Error()),
)
return "", diags
}
}
if len(response.CurrentVersion) == 0 {
diags.AddError("Unable to determine API Endpoint", "The controller endpoint is missing from response")
return "", diags
}
return response.CurrentVersion, diags
}

// Client -
type AAPClient struct {
HostURL string
Username *string
Password *string
httpClient *http.Client
HostURL string
Username *string
Password *string
httpClient *http.Client
ApiEndpoint string
}

// NewClient - create new AAPClient instance
func NewClient(host string, username *string, password *string, insecureSkipVerify bool, timeout int64) (*AAPClient, error) {
func NewClient(host string, username *string, password *string, insecureSkipVerify bool, timeout int64) (*AAPClient, diag.Diagnostics) {
hostURL, _ := url.JoinPath(host, "/")
client := AAPClient{
HostURL: hostURL,
Expand All @@ -42,7 +90,22 @@ func NewClient(host string, username *string, password *string, insecureSkipVeri
}
client.httpClient = &http.Client{Transport: tr, Timeout: time.Duration(timeout) * time.Second}

return &client, nil
// Set AAP API endpoint
diags := client.setApiEndpoint()
return &client, diags
}

func (c *AAPClient) setApiEndpoint() diag.Diagnostics {
endpoint, diags := readApiEndpoint(c)
if diags.HasError() {
return diags
}
c.ApiEndpoint = endpoint
return diags
}

func (c *AAPClient) getApiEndpoint() string {
return c.ApiEndpoint
}

func (c *AAPClient) computeURLPath(path string) string {
Expand Down
54 changes: 50 additions & 4 deletions internal/provider/client_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package provider

import (
"net/http"
"net/http/httptest"
"testing"

"fmt"
Expand All @@ -22,12 +24,56 @@ func TestComputeURLPath(t *testing.T) {
var expected = "https://localhost:8043/api/v2/state/"
for _, tc := range testTable {
t.Run(tc.name, func(t *testing.T) {
client, err := NewClient(tc.url, nil, nil, true, 0)
if err != nil {
t.Fatalf(`Failed to create provider client %v`, err)
client := AAPClient{
HostURL: tc.url,
Username: nil,
Password: nil,
httpClient: nil,
ApiEndpoint: "",
}
result := client.computeURLPath(tc.path)
assert.Equal(t, result, expected, fmt.Sprintf("expected (%s), got (%s)", expected, result))
assert.Equal(t, expected, result, fmt.Sprintf("expected (%s), got (%s)", expected, result))
})
}
}

func TestReadApiEndpoint(t *testing.T) {
server_24 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/" {
t.Errorf("Expected to request '/api/', got: %s", r.URL.Path)
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"current_version": "/api/v2/"}`)) //nolint:errcheck
}))
defer server_24.Close()

server_25 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/":
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"apis":{"gateway": "/api/gateway/", "controller": "/api/controller/"}}`)) //nolint:errcheck
case "/api/controller/":
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"current_version": "/api/controller/v2/"}`)) //nolint:errcheck
default:
t.Errorf("Expected to request one of '/api/', '/api/controller/', got: %s", r.URL.Path)
}
}))
defer server_25.Close()

testTable := []struct {
Name string
URL string
expected string
}{
{Name: "AAP 2.4", URL: server_24.URL, expected: "/api/v2/"},
{Name: "AAP 2.5+", URL: server_25.URL, expected: "/api/controller/v2/"},
}
for _, tc := range testTable {
t.Run(tc.Name, func(t *testing.T) {
client, diags := NewClient(tc.URL, nil, nil, true, 0) // readApiEndpoint() is called when creating client
assert.Equal(t, false, diags.HasError(), fmt.Sprintf("readApiEndpoint() returns errors (%v)", diags))
assert.Equal(t, tc.expected, client.getApiEndpoint())
})
}
}
4 changes: 3 additions & 1 deletion internal/provider/group_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"path"

"github.com/ansible/terraform-provider-aap/internal/provider/customtypes"
"github.com/hashicorp/terraform-plugin-framework/diag"
Expand Down Expand Up @@ -137,7 +138,8 @@ func (r *GroupResource) Create(ctx context.Context, req resource.CreateRequest,
requestData := bytes.NewReader(createRequestBody)

// Create new group in AAP
createResponseBody, diags := r.client.Create("/api/v2/groups/", requestData)
groupsURL := path.Join(r.client.getApiEndpoint(), "groups")
createResponseBody, diags := r.client.Create(groupsURL, requestData)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
Expand Down
4 changes: 3 additions & 1 deletion internal/provider/host_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"path"
"slices"
"sync"

Expand Down Expand Up @@ -162,7 +163,8 @@ func (r *HostResource) Create(ctx context.Context, req resource.CreateRequest, r
requestData := bytes.NewReader(createRequestBody)

// Create new host in AAP
createResponseBody, diags := r.client.Create("/api/v2/hosts/", requestData)
hostsURL := path.Join(r.client.getApiEndpoint(), "hosts")
createResponseBody, diags := r.client.Create(hostsURL, requestData)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
Expand Down
4 changes: 3 additions & 1 deletion internal/provider/inventory_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"path"

"github.com/ansible/terraform-provider-aap/internal/provider/customtypes"
"github.com/hashicorp/terraform-plugin-framework/datasource"
Expand Down Expand Up @@ -78,7 +79,8 @@ func (d *InventoryDataSource) Read(ctx context.Context, req datasource.ReadReque
return
}

readResponseBody, diags := d.client.Get("api/v2/inventories/" + state.Id.String())
resourceURL := path.Join(d.client.getApiEndpoint(), "inventories", state.Id.String())
readResponseBody, diags := d.client.Get(resourceURL)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
Expand Down
4 changes: 3 additions & 1 deletion internal/provider/inventory_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"path"

"github.com/ansible/terraform-provider-aap/internal/provider/customtypes"
"github.com/hashicorp/terraform-plugin-framework/diag"
Expand Down Expand Up @@ -122,7 +123,8 @@ func (r *InventoryResource) Create(ctx context.Context, req resource.CreateReque
requestData := bytes.NewReader(createRequestBody)

// Create new inventory in AAP
createResponseBody, diags := r.client.Create("/api/v2/inventories/", requestData)
inventoriesURL := path.Join(r.client.getApiEndpoint(), "inventories")
createResponseBody, diags := r.client.Create(inventoriesURL, requestData)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
Expand Down
3 changes: 2 additions & 1 deletion internal/provider/job_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"path"

"github.com/ansible/terraform-provider-aap/internal/provider/customtypes"
"github.com/hashicorp/terraform-plugin-framework/attr"
Expand Down Expand Up @@ -302,7 +303,7 @@ func (r *JobResource) LaunchJob(data *JobResourceModel) diag.Diagnostics {
}

requestData := bytes.NewReader(requestBody)
var postURL = "/api/v2/job_templates/" + data.GetTemplateID() + "/launch/"
var postURL = path.Join(r.client.getApiEndpoint(), "job_templates", data.GetTemplateID(), "launch")
resp, body, err := r.client.doRequest(http.MethodPost, postURL, requestData)
diags.Append(ValidateResponse(resp, body, err, []int{http.StatusCreated})...)
if diags.HasError() {
Expand Down
12 changes: 2 additions & 10 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,8 @@ func (p *aapProvider) Configure(ctx context.Context, req provider.ConfigureReque
}

// Create a new http client using the configuration values
client, err := NewClient(host, &username, &password, insecureSkipVerify, timeout)
if err != nil {
resp.Diagnostics.AddError(
"Unable to Create AAP API Client",
"An unexpected error occurred when creating the AAP API client. "+
"If the error is not clear, please contact the provider developers.\n\n"+
"http Client Error: "+err.Error(),
)
return
}
client, diags := NewClient(host, &username, &password, insecureSkipVerify, timeout)
resp.Diagnostics.Append(diags...)

// Make the http client available during DataSource and Resource
// type Configure methods.
Expand Down
11 changes: 5 additions & 6 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,17 @@ func testGetResource(urlPath string) ([]byte, error) {
username := os.Getenv("AAP_USERNAME")
password := os.Getenv("AAP_PASSWORD")

client, err := NewClient(host, &username, &password, true, 0)
if err != nil {
return nil, err
client, diags := NewClient(host, &username, &password, true, 0)
if diags.HasError() {
return nil, fmt.Errorf("%v", diags.Errors())
}

body, diags := client.Get(urlPath)
if diags.HasError() {
err = fmt.Errorf("%v", diags.Errors())
return nil, err
return nil, fmt.Errorf("%v", diags.Errors())
}

return body, err
return body, nil
}

func TestReadValues(t *testing.T) {
Expand Down
Loading