Skip to content

Commit

Permalink
Merge pull request #447 from Aflynn50/channel-validator
Browse files Browse the repository at this point in the history
Validate channel in application resource
  • Loading branch information
Aflynn50 authored Apr 18, 2024
2 parents d27c343 + c216fec commit 37d39c3
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 8 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ endif

JUJU=juju
CONTROLLER=$(shell ${JUJU} whoami | yq .Controller)
CONTROLLER_ADDRESSES="$(shell ${JUJU} show-controller | yq .${CONTROLLER}.details.\"api-endpoints\" | tr -d "[]' "|tr -d '"'|tr -d '\n')"
USERNAME="$(shell cat ~/.local/share/juju/accounts.yaml | yq .controllers.${CONTROLLER}.user|tr -d '"')"
PASSWORD="$(shell cat ~/.local/share/juju/accounts.yaml | yq .controllers.${CONTROLLER}.password|tr -d '"')"
CA_CERT="$(shell ${JUJU} show-controller $(echo ${CONTROLLER}|tr -d '"')| yq .${CONTROLLER}.details.\"ca-cert\"|tr -d '"'|sed 's/\\n/\n/g')"
CONTROLLER_ADDRESSES="$(shell ${JUJU} show-controller | yq '.${CONTROLLER}.details."api-endpoints"' | tr -d "[]' "|tr -d '"'|tr -d '\n')"
USERNAME="$(shell cat ~/.local/share/juju/accounts.yaml | yq '.controllers.${CONTROLLER}.user'|tr -d '"')"
PASSWORD="$(shell cat ~/.local/share/juju/accounts.yaml | yq '.controllers.${CONTROLLER}.password'|tr -d '"')"
CA_CERT="$(shell ${JUJU} show-controller $(echo ${CONTROLLER}|tr -d '"')| yq '.${CONTROLLER}.details."ca-cert"'|tr -d '"'|sed 's/\\n/\n/g')"

.PHONY: juju-unit-test
juju-unit-test:
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Terraform 0.13 and later:
terraform {
required_providers {
juju = {
version = "~> 0.10.0"
version = "~> 0.11.0"
source = "juju/juju"
}
}
Expand Down Expand Up @@ -149,7 +149,7 @@ resource "juju_integration" "wp_to_percona" {
Terraform 0.12 and earlier:
```terraform
provider "juju" {
version = "~> 0.10.0"
version = "~> 0.11.0"
controller_addresses = "10.225.205.241:17070,10.225.205.242:17070"
Expand Down
2 changes: 1 addition & 1 deletion examples/provider/provider.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
terraform {
required_providers {
juju = {
version = "~> 0.10.0"
version = "~> 0.11.0"
source = "juju/juju"
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/provider/provider_0.12.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
provider "juju" {
version = "~> 0.10.0"
version = "~> 0.11.0"

controller_addresses = "10.225.205.241:17070,10.225.205.242:17070"

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/ansiterm v1.0.0 // indirect
github.com/juju/blobstore/v3 v3.0.2 // indirect
github.com/juju/charm/v11 v11.1.0 // indirect
github.com/juju/description/v5 v5.0.4 // indirect
github.com/juju/featureflag v1.0.0 // indirect
github.com/juju/gnuflag v1.0.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ github.com/juju/ansiterm v1.0.0 h1:gmMvnZRq7JZJx6jkfSq9/+2LMrVEwGwt7UR6G+lmDEg=
github.com/juju/ansiterm v1.0.0/go.mod h1:PyXUpnI3olx3bsPcHt98FGPX/KCFZ1Fi+hw1XLI6384=
github.com/juju/blobstore/v3 v3.0.2 h1:roZ4YBuZYmWId6y/6ZLQSAMmNlHOclHD8PQAMOQer6E=
github.com/juju/blobstore/v3 v3.0.2/go.mod h1:NXEgMhrVH5744/zLfSkzsySlDQUpCgzvcNxjJJhICko=
github.com/juju/charm/v11 v11.1.0 h1:YTvFRugIhRMAe4z6Vr7Acw9oKnJBNfpwN9yTOqJv3r0=
github.com/juju/charm/v11 v11.1.0/go.mod h1:Mge5Ko3pPgocmk4v1pQgmBhF8BuBLGTCFu3jq83JvHk=
github.com/juju/charm/v12 v12.0.0 h1:/h3YRMqbgxT89QkQGgMS/myOxuHy/kzBLCDOvodsoFY=
github.com/juju/charm/v12 v12.0.0/go.mod h1:rX3no84EHT+qN+BGtwqPyvueC1Sxr0bXWxsbUd6i1iY=
github.com/juju/clock v1.0.3 h1:yJHIsWXeU8j3QcBdiess09SzfiXRRrsjKPn2whnMeds=
Expand Down
4 changes: 4 additions & 0 deletions internal/provider/resource_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/juju/errors"

"github.com/juju/juju/core/constraints"

"github.com/juju/terraform-provider-juju/internal/juju"
Expand Down Expand Up @@ -238,6 +239,9 @@ func (r *applicationResource) Schema(_ context.Context, _ resource.SchemaRequest
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
Validators: []validator.String{
StringIsChannelValidator{},
},
},
"revision": schema.Int64Attribute{
Description: "The revision of the charm to deploy. During the update phase, the charm revision should be update before config update, to avoid issues with config parameters parsing.",
Expand Down
1 change: 1 addition & 0 deletions internal/provider/validator_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"

"github.com/hashicorp/terraform-plugin-framework/schema/validator"

"github.com/juju/juju/core/base"
)

Expand Down
47 changes: 47 additions & 0 deletions internal/provider/validator_channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2024 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package provider

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/juju/charm/v11"
)

type StringIsChannelValidator struct{}

// Description returns a plain text description of the validator's behavior, suitable for a practitioner to understand its impact.
func (v StringIsChannelValidator) Description(context.Context) string {
return "string must conform to track/risk or track/risk/branch e.g. latest/stable"
}

// MarkdownDescription returns a markdown formatted description of the validator's behavior, suitable for a practitioner to understand its impact.
func (v StringIsChannelValidator) MarkdownDescription(context.Context) string {
return "string must conform to track/risk or track/risk/branch e.g. latest/stable"
}

// Validate runs the main validation logic of the validator, reading configuration data out of `req` and updating `resp` with diagnostics.
func (v StringIsChannelValidator) ValidateString(_ context.Context, req validator.StringRequest, resp *validator.StringResponse) {
// If the value is unknown or null, there is nothing to validate.
if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() {
return
}

if channel, err := charm.ParseChannel(req.ConfigValue.ValueString()); err != nil {
resp.Diagnostics.AddAttributeError(
req.Path,
"Invalid Channel",
err.Error(),
)
return
} else if channel.Track == "" || channel.Risk == "" {
resp.Diagnostics.AddAttributeError(
req.Path,
"Invalid Channel",
"String must conform to track/risk or track/risk/branch, e.g. latest/stable",
)
return
}
}
73 changes: 73 additions & 0 deletions internal/provider/validator_channel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2024 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package provider_test

import (
"context"
"testing"

"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/juju/terraform-provider-juju/internal/provider"
)

func TestChannelValidatorValid(t *testing.T) {
validChannels := []types.String{
types.StringValue("track/stable"),
types.StringValue("track/edge/branch"),
types.StringNull(),
types.StringUnknown(),
}

channelValidator := provider.StringIsChannelValidator{}
for _, channel := range validChannels {
req := validator.StringRequest{
ConfigValue: channel,
}
var resp validator.StringResponse
channelValidator.ValidateString(context.Background(), req, &resp)

if resp.Diagnostics.HasError() {
t.Errorf("errors %v", resp.Diagnostics.Errors())
}
}
}

func TestChannelValidatorInvalid(t *testing.T) {
invalidChannels := []struct {
str types.String
err string
}{{
str: types.StringValue("track"),
err: "String must conform to track/risk or track/risk/branch, e.g. latest/stable",
}, {
str: types.StringValue("edge"),
err: "String must conform to track/risk or track/risk/branch, e.g. latest/stable",
}, {
str: types.StringValue(`track\risk`),
err: "String must conform to track/risk or track/risk/branch, e.g. latest/stable",
}, {
str: types.StringValue(`track/invalidrisk`),
err: `risk in channel "track/invalidrisk" not valid`,
}, {
str: types.StringValue(`track/invalidrisk/branch`),
err: `risk in channel "track/invalidrisk/branch" not valid`,
}}

channelValidator := provider.StringIsChannelValidator{}
for _, test := range invalidChannels {
req := validator.StringRequest{
ConfigValue: test.str,
}
var resp validator.StringResponse
channelValidator.ValidateString(context.Background(), req, &resp)

if c := resp.Diagnostics.ErrorsCount(); c != 1 {
t.Errorf("expected one error, got %d", c)
}
if deets := resp.Diagnostics.Errors()[0].Detail(); deets != test.err {
t.Errorf("expected error %q, got %q", test.err, deets)
}
}
}

0 comments on commit 37d39c3

Please sign in to comment.