Skip to content

Commit

Permalink
Merge pull request #70 from paketo-buildpacks/jtc/support-keys-withou…
Browse files Browse the repository at this point in the history
…t-hyphens

Support token and url keys without hyphens
  • Loading branch information
dmikusa authored Aug 4, 2023
2 parents d809eb0 + 294778c commit 1c47e07
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 63 deletions.
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

0 comments on commit 1c47e07

Please sign in to comment.