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

Validate channel in application resource #447

Merged
merged 5 commits into from
Apr 18, 2024
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
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)
}
}
}
Loading