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

Support token and url keys without hyphens #70

Merged
merged 7 commits into from
Aug 4, 2023
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ bin/
dependencies/
package/
scratch/
.idea/
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,41 @@
The Paketo Buildpack for Dynatrace is a Cloud Native Buildpack that contributes the Dynatrace OneAgent and configures it to connect to the service.

## Behavior
This buildpack will participate if either of the following conditions is met
This buildpack will participate if either of the following conditions is met:

* A binding exists with `name` containing `Dynatrace`
* A binding exists with `type` of `Dynatrace`

**Note**: While a single binding may match both conditions, you may *not* have multiple bindings that match the conditions above. Multiple Dynatrace service bindings are not supported for a single application.
**Note**:
While a single binding may match both conditions, you may *not* have multiple bindings that match the conditions above. Multiple Dynatrace service bindings are not supported for a single application.

**Note**: The binding must include the following required Secret values to successfully contribute Dynatrace
**Note**:
The binding must include the following required Secret values to successfully contribute Dynatrace


| Key | Value Description
| -------------------- | -----------
| `api-url`<br/> **or** <br/> `environment-id` | The base URL of the Dynatrace API. If not set, `environment-id` must be set. <br/> --- <br/> If `api-url` is not set, a URL is configured in the form: https://<`environment-id`>.live.dynatrace.com/api
| `api-token` | (Required) The token for communicating with the Dynatrace service.
| Key | Value Description |
|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `api-url`<br/> **or** <br/> `environment-id` | The base URL of the Dynatrace API. If not set, `environment-id` must be set. <br/> --- <br/> If `api-url` is not set, a URL is configured in the form: https://<`environment-id`>.live.dynatrace.com/api |
| `api-token` | (Required) The token for communicating with the Dynatrace service. |

**Note**:
the API URL and API token secret keys support multiple casing options for ease of integration.
This buildpack will choose to use `api-url` over `apiurl` and `api-token` over `apitoken` if both are set.

The buildpack will do the following for .NET, Go, Apache HTTPd, Java, Nginx, NodeJS, and PHP applications:

* Contributes a OneAgent including the appropriate libraries to a layer and configures `$LD_PRELOAD` to use it
* Sets `$DT_TENANT`, `$DT_TENANTTOKEN`, and `$DT_CONNECTION_POINT` at launch time.
* Transforms the contents of the binding secret to environment variables with the pattern `DT_<KEY>=<VALUE>`
* Excluding `api-token`, `api-url`, and `environment-id`
* Excluding `api-token`, `apitoken`, `api-url`, `apiurl`, and `environment-id`

## Bindings
The buildpack optionally accepts the following bindings:

### Type: `dependency-mapping`
|Key | Value | Description
|----------------------|---------|------------
|`<dependency-digest>` | `<uri>` | If needed, the buildpack will fetch the dependency with digest `<dependency-digest>` from `<uri>`
| Key | Value | Description |
|-----------------------|---------|---------------------------------------------------------------------------------------------------|
| `<dependency-digest>` | `<uri>` | If needed, the buildpack will fetch the dependency with digest `<dependency-digest>` from `<uri>` |

## License

Expand Down
43 changes: 0 additions & 43 deletions dt/base_uri_test.go

This file was deleted.

12 changes: 11 additions & 1 deletion dt/base_uri.go → dt/binding_secrets.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,5 +26,15 @@ func BaseURI(binding libcnb.Binding) string {
if s, ok := binding.Secret["api-url"]; ok {
return s
}
if s, ok := binding.Secret["apiurl"]; ok {
return s
}
return fmt.Sprintf("https://%s.live.dynatrace.com/api", binding.Secret["environment-id"])
}

func APIToken(binding libcnb.Binding) string {
if s, ok := binding.Secret["api-token"]; ok {
return s
}
return binding.Secret["apitoken"]
}
84 changes: 84 additions & 0 deletions dt/binding_secrets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dt_test

import (
"testing"

"github.com/buildpacks/libcnb"
. "github.com/onsi/gomega"
"github.com/sclevine/spec"

"github.com/paketo-buildpacks/dynatrace/v4/dt"
)

var createBinding = func(keysAndValues ...string) libcnb.Binding {
secrets := make(map[string]string, len(keysAndValues)/2)

for i := 0; i < len(keysAndValues); i = i + 2 {
secrets[keysAndValues[i]] = keysAndValues[i+1]
}

return libcnb.Binding{Secret: secrets}
}

func testBaseURI(t *testing.T, _ spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect
)

it("uses api-url", func() {
Expect(dt.BaseURI(createBinding("api-url", "test-url"))).
To(Equal("test-url"))
})

it("uses api-url when both api-url and apiurl are set", func() {
Expect(dt.BaseURI(createBinding("api-url", "test-url", "apiurl", "other-url"))).
To(Equal("test-url"))
})

it("uses apiurl", func() {
Expect(dt.BaseURI(createBinding("apiurl", "other-url"))).
To(Equal("other-url"))
})

it("uses environment-id", func() {
Expect(dt.BaseURI(createBinding("environment-id", "test-id"))).
To(Equal("https://test-id.live.dynatrace.com/api"))
})
}

func testAPIToken(t *testing.T, _ spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect
)

it("uses api-token", func() {
Expect(dt.APIToken(createBinding("api-token", "test-token"))).
To(Equal("test-token"))
})

it("uses api-token when both api-token and apitoken are set", func() {
Expect(dt.APIToken(createBinding("api-token", "test-token", "apitoken", "other-token"))).
To(Equal("test-token"))
})

it("uses apitoken", func() {
Expect(dt.APIToken(createBinding("apitoken", "other-token"))).
To(Equal("other-token"))
})
}
6 changes: 3 additions & 3 deletions dt/build.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -74,7 +74,7 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
CPEs: []string{fmt.Sprintf("cpe:2.3:a:dynatrace:one-agent:%s:*:*:*:*:*:*:*", v)},
}

a, be := NewAgent(dep, dc, s.Secret["api-token"], context.Buildpack.Info)
a, be := NewAgent(dep, dc, APIToken(s), context.Buildpack.Info)
a.Logger = b.Logger
result.Layers = append(result.Layers, a)
result.BOM.Entries = append(result.BOM.Entries, be)
Expand All @@ -94,7 +94,7 @@ func (Build) AgentVersion(binding libcnb.Binding, info libcnb.BuildpackInfo) (st
if err != nil {
return "", fmt.Errorf("unable to create new GET request for %s\n%w", uri, err)
}
req.Header.Set("Authorization", fmt.Sprintf("Api-Token %s", binding.Secret["api-token"]))
req.Header.Set("Authorization", fmt.Sprintf("Api-Token %s", APIToken(binding)))
req.Header.Set("User-Agent", fmt.Sprintf("%s/%s", info.ID, info.Version))

client := http.Client{Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}}
Expand Down
28 changes: 27 additions & 1 deletion dt/build_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -151,4 +151,30 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
verifyBOM(result.BOM)
})

it("supports apitoken and apiurl", func() {
ctx.Platform.Bindings = libcnb.Bindings{
{
Name: "DynatraceBinding",
Type: "user-provided",
Secret: map[string]string{
"apitoken": "custom-apitoken",
"apiurl": server.URL(),
},
},
}

server.SetHandler(0, ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/v1/deployment/installer/agent/unix/paas/latest/metainfo"),
ghttp.VerifyHeaderKV("Authorization", "Api-Token custom-apitoken"),
ghttp.VerifyHeaderKV("User-Agent", "test-id/test-version"),
ghttp.RespondWithJSONEncoded(http.StatusOK, map[string]interface{}{"latestAgentVersion": "test-version"}),
))

result, err := dt.Build{}.Build(ctx)
Expect(err).NotTo(HaveOccurred())

verifyLayers(result.Layers, server.URL())
verifyBOM(result.BOM)
})

}
3 changes: 2 additions & 1 deletion dt/init_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,6 +27,7 @@ func TestUnit(t *testing.T) {
suite := spec.New("dynatrace", spec.Report(report.Terminal{}))
suite("Agent", testAgent)
suite("BaseURI", testBaseURI)
suite("APIToken", testAPIToken)
suite("Build", testBuild)
suite("Detect", testDetect)
suite.Run(t)
Expand Down
6 changes: 4 additions & 2 deletions helper/properties.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -64,7 +64,7 @@ func (p Properties) Execute() (map[string]string, error) {
return nil, fmt.Errorf("unable to create new GET request for %s\n%w", uri, err)
}
req.Header.Set("User-Agent", fmt.Sprintf("%s/%s", id, version))
req.Header.Set("Authorization", fmt.Sprintf("Api-Token %s", b.Secret["api-token"]))
req.Header.Set("Authorization", fmt.Sprintf("Api-Token %s", dt.APIToken(b)))

client := http.Client{Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}}
resp, err := client.Do(req)
Expand Down Expand Up @@ -92,7 +92,9 @@ func (p Properties) Execute() (map[string]string, error) {
e["DT_CONNECTION_POINT"] = strings.Join(raw.CommunicationsEndpoints, ";")

delete(b.Secret, "api-token")
delete(b.Secret, "apitoken")
delete(b.Secret, "api-url")
delete(b.Secret, "apiurl")
delete(b.Secret, "environment-id")

for k, v := range b.Secret {
Expand Down
34 changes: 33 additions & 1 deletion helper/properties_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -144,6 +144,38 @@ func testProperties(t *testing.T, context spec.G, it spec.S) {
"DT_TEST_KEY": "custom-test-value",
}))
})

it("contributes properties if apitoken and apiurl are used", func() {
p.Bindings = libcnb.Bindings{
{
Name: "DynatraceBinding",
Type: "user-provided",
Secret: map[string]string{
"apitoken": "custom-test-apitoken",
"apiurl": server.URL(),
"test-key": "custom-test-value",
},
},
}

server.AppendHandlers(ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/v1/deployment/installer/agent/connectioninfo"),
ghttp.VerifyHeaderKV("Authorization", "Api-Token custom-test-apitoken"),
ghttp.VerifyHeaderKV("User-Agent", "test-id/test-version"),
ghttp.RespondWithJSONEncoded(http.StatusOK, map[string]interface{}{
"tenantUUID": "test-tenant-uuid",
"tenantToken": "test-tenant-token",
"communicationEndpoints": []string{"test-communication-endpoint-1", "test-communication-endpoint-2"},
}),
))

Expect(p.Execute()).To(Equal(map[string]string{
"DT_CONNECTION_POINT": "test-communication-endpoint-1;test-communication-endpoint-2",
"DT_TENANT": "test-tenant-uuid",
"DT_TENANTTOKEN": "test-tenant-token",
"DT_TEST_KEY": "custom-test-value",
}))
})
})
})
})
Expand Down