Skip to content

Commit

Permalink
Support configuring provider via environment variables (#1)
Browse files Browse the repository at this point in the history
We should be able to set `base_url` and `default_namespace` via
environment variables. It will add more flexibility for the provider
users, as well as allow us to make the acceptance tests more dynamic.

In addition, let's add `docker-compose` to support testing with local
instance.
  • Loading branch information
tdabasinskas authored Jan 27, 2023
2 parents 0c0ab0e + e6c4565 commit af3882c
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 21 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ name: Tests
# Optionally, you can turn it on using a schedule for regular testing.
on:
pull_request:
branches: [ "main" ]
paths-ignore:
- 'README.md'
push:
branches: [ "main" ]
paths-ignore:
- 'README.md'

# Testing only needs permissions to read the repository contents.
permissions:
contents: read

concurrency:
group: Tests-${{ github.ref }}
cancel-in-progress: true

jobs:
# Ensure project builds before running testing matrix
build:
Expand Down Expand Up @@ -50,6 +56,11 @@ jobs:
name: Acceptance Tests
needs: build
runs-on: ubuntu-latest
services:
backstage:
image: roadiehq/community-backstage-image
ports:
- 7000
timeout-minutes: 15
strategy:
fail-fast: false
Expand All @@ -72,5 +83,12 @@ jobs:
- run: go mod download
- env:
TF_ACC: "1"
ACCTEST_SKIP_RESOURCE_TEST: "1"
BACKSTAGE_BASE_URL: "https://demo.backstage.io"
run: go test -v -cover ./backstage
timeout-minutes: 10
- env:
TF_ACC: "1"
BACKSTAGE_BASE_URL: "http://localhost:${{ job.services.backstage.ports[7000] }}"
run: go test -v -cover ./backstage/resource_location_test.go
timeout-minutes: 10
8 changes: 7 additions & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
default: testacc

ifdef BACKSTAGE_BASE_URL
BACKSTAGE_BASE_URL := $(BACKSTAGE_BASE_URL)
else
BACKSTAGE_BASE_URL := http://demo.backstage.io
endif

# Run acceptance tests
.PHONY: testacc
testacc:
TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m -cover
TF_ACC=1 BACKSTAGE_BASE_URL=${BACKSTAGE_BASE_URL} go test ./... -v $(TESTARGS) -timeout 120m -cover
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ most of the official documentation on developing this provider is also applicabl
- [Terraform](https://www.terraform.io/downloads)
- [Go](https://go.dev/doc/install) (1.19)
- [GNU make](https://www.gnu.org/software/make/)
- [Docker](https://docs.docker.com/get-docker/) (optional)

### Building

Expand All @@ -40,7 +41,25 @@ In order to test the provider, run the following command:
make testacc
```

This will run acceptance tests against the provider, actually spawning terraform and the provider.
This will run acceptance tests against the provider , actually spawning terraform and the provider, using `https://demo.backstage.io` as the Backstage instance. The instance can
be changed by setting the `BACKSTAGE_BASE_URL` environment variable, e.g.:

```bash
BACKSTAGE_BASE_URL=https://localhost:3000 make testacc
```

The project contains a [`docker-compose.yml`](./docker-compose.yml) file that can be used to spin up a local instance of Backstage, which can be used for testing. To do so, run:

```bash
docker-compose up -d
```

Some tests (i.e. resource tests) do not work with the public [demo instance](https://demo.backstage.io) of Backstage, as they modify the data. To skip those tests while using
the demo instance, run:

```bash
ACCTEST_SKIP_RESOURCE_TEST=1 make testacc
```

### Generating documentation

Expand All @@ -62,4 +81,4 @@ Thank you for your interest in improving the project.

## License

This provider is distributed under the Mozilla Public License v2.0 license found in the [LICENSE](./LICENSE) file.
This provider is distributed under the Mozilla Public License v2.0 license found in the [`LICENSE`](./LICENSE) file.
46 changes: 34 additions & 12 deletions backstage/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package backstage
import (
"context"
"fmt"
"os"
"regexp"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
Expand Down Expand Up @@ -31,8 +32,12 @@ type backstageProviderModel struct {
}

const (
descriptionProviderBaseURL = "Base URL of the Backstage instance, e.g. https://demo.backstage.io."
descriptionProviderDefaultNamespace = "Name of default namespace for entities (`default`, if not set)."
envBaseURL = "BACKSTAGE_BASE_URL"
envDefaultNamespace = "BACKSTAGE_DEFAULT_NAMESPACE"
descriptionProviderBaseURL = "Base URL of the Backstage instance, e.g. https://demo.backstage.io. May also be provided via `" + envBaseURL +
"` environment variable."
descriptionProviderDefaultNamespace = "Name of default namespace for entities (`default`, if not set). May also be provided via `" + envDefaultNamespace +
"` environment variable."
)

// Metadata returns the provider type name.
Expand All @@ -51,13 +56,13 @@ func (p *backstageProvider) Schema(_ context.Context, _ provider.SchemaRequest,
"Interested in the provider's latest features, or want to make sure you're up to date? Check out the " +
"[releases](https://github.com/tdabasinskas/terraform-provider-backstage/releases) for version information and release notes.",
Attributes: map[string]schema.Attribute{
"base_url": schema.StringAttribute{Required: true, Description: descriptionProviderBaseURL, Validators: []validator.String{
"base_url": schema.StringAttribute{Optional: true, MarkdownDescription: descriptionProviderBaseURL, Validators: []validator.String{
stringvalidator.RegexMatches(
regexp.MustCompile(`https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)`),
"must be a valid URL",
),
}},
"default_namespace": schema.StringAttribute{Optional: true, Description: descriptionProviderDefaultNamespace, Validators: []validator.String{
"default_namespace": schema.StringAttribute{Optional: true, MarkdownDescription: descriptionProviderDefaultNamespace, Validators: []validator.String{
stringvalidator.LengthBetween(1, 63),
stringvalidator.RegexMatches(
regexp.MustCompile(patternEntityName),
Expand All @@ -79,23 +84,40 @@ func (p *backstageProvider) Configure(ctx context.Context, req provider.Configur
return
}

baseURL := config.BaseURL.ValueString()
if config.BaseURL.IsUnknown() || config.BaseURL.IsNull() {
resp.Diagnostics.AddAttributeError(path.Root("base_url"),
"Unknown Base URL of Backstage instance", "Either target apply the source of the value first, or set the value statically in the configuration")
if config.BaseURL.IsUnknown() {
resp.Diagnostics.AddAttributeError(path.Root("base_url"), "Unknown Base URL of Backstage instance", fmt.Sprintf(
"Either target apply the source of the value first, set the value statically in the configuration, or use the %s environment variable.", envBaseURL))
}

if config.DefaultNamespace.IsUnknown() {
resp.Diagnostics.AddAttributeError(path.Root("default_namespace"),
"Unknown default entities namespace of Backstage instance",
"Either target apply the source of the value first, or set the value statically in the configuration")
resp.Diagnostics.AddAttributeError(path.Root("default_namespace"), "Unknown default entities namespace of Backstage instance", fmt.Sprintf(
"Either target apply the source of the value first, set the value statically in the configuration, or use the %s environment variable.", envDefaultNamespace))
}

defaultNamespace := config.DefaultNamespace.ValueString()
if resp.Diagnostics.HasError() {
return
}

baseURL := os.Getenv(envBaseURL)
if !config.BaseURL.IsNull() {
baseURL = config.BaseURL.ValueString()
}

defaultNamespace := os.Getenv(envDefaultNamespace)
if !config.DefaultNamespace.IsNull() {
defaultNamespace = config.DefaultNamespace.ValueString()
}
if defaultNamespace == "" {
defaultNamespace = backstage.DefaultNamespaceName
}

if baseURL == "" {
resp.Diagnostics.AddAttributeError(path.Root("base_url"), "Missing Base URL of Backstage instance", fmt.Sprintf(
"The provider cannot create the Backstage API client as there is a missing or empty value for the Backstage Base URL. Set the host value in the "+
"configuration or use the %s environment variable. If either is already set, ensure the value is not empty.", envBaseURL))

}

if resp.Diagnostics.HasError() {
return
}
Expand Down
1 change: 0 additions & 1 deletion backstage/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

const testAccProviderConfig = `
provider "backstage" {
base_url = "https://demo.backstage.io"
}
`

Expand Down
1 change: 1 addition & 0 deletions backstage/resource_location.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package backstage
12 changes: 12 additions & 0 deletions backstage/resource_location_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package backstage

import (
"os"
"testing"
)

func TestAccResourceLocation(t *testing.T) {
if os.Getenv("ACCTEST_SKIP_RESOURCE_TEST") != "" {
t.Skip("Skipping as ACCTEST_SKIP_RESOURCE_LOCATION is set")
}
}
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: '3.8'
services:
backstage:
image: roadiehq/community-backstage-image:latest
ports:
- '3000:7000'
environment:
GITHUB_TOKEN: ${GITHUB_TOKEN}
7 changes: 2 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ provider "backstage" {
<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `base_url` (String) Base URL of the Backstage instance, e.g. https://demo.backstage.io.

### Optional

- `default_namespace` (String) Name of default namespace for entities (`default`, if not set).
- `base_url` (String) Base URL of the Backstage instance, e.g. https://demo.backstage.io. May also be provided via `BACKSTAGE_BASE_URL` environment variable.
- `default_namespace` (String) Name of default namespace for entities (`default`, if not set). May also be provided via `BACKSTAGE_DEFAULT_NAMESPACE` environment variable.

0 comments on commit af3882c

Please sign in to comment.