mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-03-05 21:26:04 -05:00
terraform-provider: validate image and microservice version (#2766)
This commit is contained in:
parent
519efe637d
commit
436e7c6d3b
@ -846,7 +846,7 @@ func (c *Config) Validate(force bool) error {
|
|||||||
// Because of this we can't print the offending field name in the error message, resulting in
|
// Because of this we can't print the offending field name in the error message, resulting in
|
||||||
// suboptimal UX. Adding the field name to the struct validation of Semver would make it
|
// suboptimal UX. Adding the field name to the struct validation of Semver would make it
|
||||||
// impossible to use Semver for other fields.
|
// impossible to use Semver for other fields.
|
||||||
if err := validateMicroserviceVersion(constants.BinaryVersion(), c.MicroserviceVersion); err != nil {
|
if err := ValidateMicroserviceVersion(constants.BinaryVersion(), c.MicroserviceVersion); err != nil {
|
||||||
msg := "microserviceVersion: " + msgFromCompatibilityError(err, constants.BinaryVersion().String(), c.MicroserviceVersion.String())
|
msg := "microserviceVersion: " + msgFromCompatibilityError(err, constants.BinaryVersion().String(), c.MicroserviceVersion.String())
|
||||||
return &ValidationError{validationErrMsgs: []string{msg}}
|
return &ValidationError{validationErrMsgs: []string{msg}}
|
||||||
}
|
}
|
||||||
|
@ -690,7 +690,8 @@ func msgFromCompatibilityError(err error, binaryVersion, fieldValue string) stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateMicroserviceVersion(binaryVersion, version consemver.Semver) error {
|
// ValidateMicroserviceVersion checks that the version of the microservice is compatible with the binary version.
|
||||||
|
func ValidateMicroserviceVersion(binaryVersion, version consemver.Semver) error {
|
||||||
// Major versions always have to match.
|
// Major versions always have to match.
|
||||||
if binaryVersion.Major() != version.Major() {
|
if binaryVersion.Major() != version.Major() {
|
||||||
return compatibility.ErrMajorMismatch
|
return compatibility.ErrMajorMismatch
|
||||||
|
@ -75,7 +75,7 @@ func TestValidateMicroserviceVersion(t *testing.T) {
|
|||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
err := validateMicroserviceVersion(tc.cli, tc.services)
|
err := ValidateMicroserviceVersion(tc.cli, tc.services)
|
||||||
if tc.wantError {
|
if tc.wantError {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -93,7 +93,11 @@ go_test(
|
|||||||
"//internal/attestation/variant",
|
"//internal/attestation/variant",
|
||||||
"//internal/config",
|
"//internal/config",
|
||||||
"//internal/constants",
|
"//internal/constants",
|
||||||
|
"//internal/semver",
|
||||||
|
"//terraform-provider-constellation/internal/data",
|
||||||
|
"@com_github_hashicorp_terraform_plugin_framework//attr",
|
||||||
"@com_github_hashicorp_terraform_plugin_framework//providerserver",
|
"@com_github_hashicorp_terraform_plugin_framework//providerserver",
|
||||||
|
"@com_github_hashicorp_terraform_plugin_framework//types/basetypes",
|
||||||
"@com_github_hashicorp_terraform_plugin_go//tfprotov6",
|
"@com_github_hashicorp_terraform_plugin_go//tfprotov6",
|
||||||
"@com_github_hashicorp_terraform_plugin_testing//helper/resource",
|
"@com_github_hashicorp_terraform_plugin_testing//helper/resource",
|
||||||
"@com_github_hashicorp_terraform_plugin_testing//terraform",
|
"@com_github_hashicorp_terraform_plugin_testing//terraform",
|
||||||
|
@ -722,20 +722,11 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse OS image version
|
// parse OS image version
|
||||||
var image imageAttribute
|
image, imageSemver, convertDiags := r.getImageVersion(ctx, data)
|
||||||
convertDiags = data.Image.As(ctx, &image, basetypes.ObjectAsOptions{})
|
|
||||||
diags.Append(convertDiags...)
|
diags.Append(convertDiags...)
|
||||||
if diags.HasError() {
|
if diags.HasError() {
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
imageSemver, err := semver.New(image.Version)
|
|
||||||
if err != nil {
|
|
||||||
diags.AddAttributeError(
|
|
||||||
path.Root("image").AtName("version"),
|
|
||||||
"Invalid image version",
|
|
||||||
fmt.Sprintf("Parsing image version (%s): %s", image.Version, err))
|
|
||||||
return diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse license ID
|
// parse license ID
|
||||||
licenseID := data.LicenseID.ValueString()
|
licenseID := data.LicenseID.ValueString()
|
||||||
@ -948,6 +939,29 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ClusterResource) getImageVersion(ctx context.Context, data *ClusterResourceModel) (imageAttribute, semver.Semver, diag.Diagnostics) {
|
||||||
|
var image imageAttribute
|
||||||
|
diags := data.Image.As(ctx, &image, basetypes.ObjectAsOptions{})
|
||||||
|
if diags.HasError() {
|
||||||
|
return imageAttribute{}, semver.Semver{}, diags
|
||||||
|
}
|
||||||
|
imageSemver, err := semver.New(image.Version)
|
||||||
|
if err != nil {
|
||||||
|
diags.AddAttributeError(
|
||||||
|
path.Root("image").AtName("version"),
|
||||||
|
"Invalid image version",
|
||||||
|
fmt.Sprintf("Parsing image version (%s): %s", image.Version, err))
|
||||||
|
return imageAttribute{}, semver.Semver{}, diags
|
||||||
|
}
|
||||||
|
if err := compatibility.BinaryWith(r.providerData.Version.String(), imageSemver.String()); err != nil {
|
||||||
|
diags.AddAttributeError(
|
||||||
|
path.Root("image").AtName("version"),
|
||||||
|
"Invalid image version",
|
||||||
|
fmt.Sprintf("Image version (%s) incompatible with provider version (%s): %s", image.Version, r.providerData.Version.String(), err))
|
||||||
|
}
|
||||||
|
return image, imageSemver, diags
|
||||||
|
}
|
||||||
|
|
||||||
// initRPCPayload groups the data required to run the init RPC.
|
// initRPCPayload groups the data required to run the init RPC.
|
||||||
type initRPCPayload struct {
|
type initRPCPayload struct {
|
||||||
csp cloudprovider.Provider // cloud service provider the cluster runs on.
|
csp cloudprovider.Provider // cloud service provider the cluster runs on.
|
||||||
@ -1178,6 +1192,12 @@ func (r *ClusterResource) getMicroserviceVersion(ctx context.Context, data *Clus
|
|||||||
tflog.Info(ctx, fmt.Sprintf("No Microservice version specified. Using default version %s.", r.providerData.Version))
|
tflog.Info(ctx, fmt.Sprintf("No Microservice version specified. Using default version %s.", r.providerData.Version))
|
||||||
ver = r.providerData.Version
|
ver = r.providerData.Version
|
||||||
}
|
}
|
||||||
|
if err := config.ValidateMicroserviceVersion(r.providerData.Version, ver); err != nil {
|
||||||
|
diags.AddAttributeError(
|
||||||
|
path.Root("constellation_microservice_version"),
|
||||||
|
"Invalid microservice version",
|
||||||
|
fmt.Sprintf("Microservice version (%s) incompatible with provider version (%s): %s", ver, r.providerData.Version, err))
|
||||||
|
}
|
||||||
return ver, diags
|
return ver, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,14 +7,104 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/semver"
|
||||||
|
"github.com/edgelesssys/constellation/v2/terraform-provider-constellation/internal/data"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMicroserviceConstraint(t *testing.T) {
|
||||||
|
sut := &ClusterResource{
|
||||||
|
providerData: data.ProviderData{
|
||||||
|
Version: semver.NewFromInt(2, 15, 0, ""),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
expectedErrorCount int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "outdated by 2 minor versions is invalid",
|
||||||
|
version: "v2.13.0",
|
||||||
|
expectedErrorCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "outdated by 1 minor is allowed for upgrade",
|
||||||
|
version: "v2.14.0",
|
||||||
|
expectedErrorCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same version is valid",
|
||||||
|
version: "v2.15.0",
|
||||||
|
expectedErrorCount: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
_, diags := sut.getMicroserviceVersion(context.Background(), &ClusterResourceModel{
|
||||||
|
MicroserviceVersion: basetypes.NewStringValue(tc.version),
|
||||||
|
})
|
||||||
|
require.Equal(t, tc.expectedErrorCount, diags.ErrorsCount())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestViolatedImageConstraint(t *testing.T) {
|
||||||
|
sut := &ClusterResource{
|
||||||
|
providerData: data.ProviderData{
|
||||||
|
Version: semver.NewFromInt(2, 15, 0, ""),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
expectedErrorCount int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "outdated by 2 minor versions is invalid",
|
||||||
|
version: "v2.13.0",
|
||||||
|
expectedErrorCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "outdated by 1 minor is allowed for upgrade",
|
||||||
|
version: "v2.14.0",
|
||||||
|
expectedErrorCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same version is valid",
|
||||||
|
version: "v2.15.0",
|
||||||
|
expectedErrorCount: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
img := imageAttribute{
|
||||||
|
Version: tc.version,
|
||||||
|
}
|
||||||
|
|
||||||
|
input, diags := basetypes.NewObjectValueFrom(context.Background(), map[string]attr.Type{
|
||||||
|
"version": basetypes.StringType{},
|
||||||
|
"reference": basetypes.StringType{},
|
||||||
|
"short_path": basetypes.StringType{},
|
||||||
|
}, img)
|
||||||
|
require.Equal(t, 0, diags.ErrorsCount())
|
||||||
|
_, _, diags2 := sut.getImageVersion(context.Background(), &ClusterResourceModel{
|
||||||
|
Image: input,
|
||||||
|
})
|
||||||
|
require.Equal(t, tc.expectedErrorCount, diags2.ErrorsCount())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccClusterResourceImports(t *testing.T) {
|
func TestAccClusterResourceImports(t *testing.T) {
|
||||||
// Set the path to the Terraform binary for acceptance testing when running under Bazel.
|
// Set the path to the Terraform binary for acceptance testing when running under Bazel.
|
||||||
bazelPreCheck := func() { bazelSetTerraformBinaryPath(t) }
|
bazelPreCheck := func() { bazelSetTerraformBinaryPath(t) }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user