From 323fd226ccca8d921b0bddd9b304eaffd71925a0 Mon Sep 17 00:00:00 2001 From: gatici Date: Thu, 15 Aug 2024 20:53:18 +0300 Subject: [PATCH] revert: revert the changes which are done in deployFromRepository and legacyDeploy methods feat: Use ResourceAPIClient.Upload to upload the custom resources for the applications which are deployed with deployFromRepository method tests: Use grafana-k8s charm to validate the custom resource usage in the integration tests Signed-off-by: gatici --- internal/juju/applications.go | 135 +++++++------- internal/juju/interfaces.go | 2 + internal/juju/mock_test.go | 30 +++ internal/juju/resources.go | 175 +++++------------- internal/provider/resource_application.go | 15 +- .../provider/resource_application_test.go | 84 +++++---- 6 files changed, 191 insertions(+), 250 deletions(-) diff --git a/internal/juju/applications.go b/internal/juju/applications.go index 48b9d12d..87a78a28 100644 --- a/internal/juju/applications.go +++ b/internal/juju/applications.go @@ -13,7 +13,6 @@ import ( "errors" "fmt" "math" - "os" "reflect" "sort" "strconv" @@ -36,7 +35,6 @@ import ( apicommoncharm "github.com/juju/juju/api/common/charm" "github.com/juju/juju/cmd/juju/application/utils" resourcecmd "github.com/juju/juju/cmd/juju/resource" - "github.com/juju/juju/cmd/modelcmd" corebase "github.com/juju/juju/core/base" "github.com/juju/juju/core/constraints" "github.com/juju/juju/core/instance" @@ -324,12 +322,6 @@ type DestroyApplicationInput struct { ModelName string } -type osFilesystem struct{} - -func (osFilesystem) Open(name string) (modelcmd.ReadSeekCloser, error) { - return os.Open(name) -} - func resolveCharmURL(charmName string) (*charm.URL, error) { path, err := charm.EnsureSchema(charmName, charm.CharmHub) if err != nil { @@ -356,24 +348,15 @@ func (c applicationsClient) CreateApplication(ctx context.Context, input *Create } applicationAPIClient := apiapplication.NewClient(conn) + resourceAPIClient, err := apiresources.NewClient(conn) + if err != nil { + return nil, err + } if applicationAPIClient.BestAPIVersion() >= 19 { - resourceIDs, apiCharmID, err := c.deployFromRepository(applicationAPIClient, transformedInput, conn) + err := c.deployFromRepository(applicationAPIClient, resourceAPIClient, transformedInput) if err != nil { return nil, err } - if len(resourceIDs) != 0 { - toReturn := apiapplication.SetCharmConfig{ - ApplicationName: transformedInput.applicationName, - CharmID: apiCharmID, - ResourceIDs: resourceIDs, - } - setCharmConfig := &toReturn - - err = applicationAPIClient.SetCharm(model.GenerationMaster, *setCharmConfig) - if err != nil { - return nil, err - } - } } else { err = c.legacyDeploy(ctx, conn, applicationAPIClient, transformedInput) err = jujuerrors.Annotate(err, "legacy deploy method") @@ -391,16 +374,14 @@ func (c applicationsClient) CreateApplication(ctx context.Context, input *Create }, err } -func (c applicationsClient) deployFromRepository(applicationAPIClient *apiapplication.Client, transformedInput transformedCreateApplicationInput, conn api.Connection) (map[string]string, apiapplication.CharmID, error) { +func (c applicationsClient) deployFromRepository(applicationAPIClient ApplicationAPIClient, resourceAPIClient ResourceAPIClient, transformedInput transformedCreateApplicationInput) error { settingsForYaml := map[interface{}]interface{}{transformedInput.applicationName: transformedInput.config} configYaml, err := goyaml.Marshal(settingsForYaml) - resourceIDs := map[string]string{} - apiCharmID := apiapplication.CharmID{} if err != nil { - return resourceIDs, apiapplication.CharmID{}, jujuerrors.Trace(err) + return jujuerrors.Trace(err) } c.Tracef("Calling DeployFromRepository") - deployInfo, pendingResources, errs := applicationAPIClient.DeployFromRepository(apiapplication.DeployFromRepositoryArg{ + deployInfo, localPendingResources, errs := applicationAPIClient.DeployFromRepository(apiapplication.DeployFromRepositoryArg{ CharmName: transformedInput.charmName, ApplicationName: transformedInput.applicationName, Base: &transformedInput.charmBase, @@ -415,47 +396,20 @@ func (c applicationsClient) deployFromRepository(applicationAPIClient *apiapplic Resources: transformedInput.resources, Storage: transformedInput.storage, }) - if errs != nil { - return resourceIDs, apiCharmID, errors.Join(errs...) - } - charmsAPIClient := apicharms.NewClient(conn) - modelconfigAPIClient := apimodelconfig.NewClient(conn) - resolvedURL, resolvedOrigin, supportedBases, err := getCharmResolvedUrlAndOrigin(conn, transformedInput) - if err != nil { - return resourceIDs, apiCharmID, err + if len(errs) != 0 { + return errors.Join(errs...) } - userSuppliedBase := transformedInput.charmBase - baseToUse, err := c.baseToUse(modelconfigAPIClient, userSuppliedBase, resolvedOrigin.Base, supportedBases) - if err != nil { - return resourceIDs, apiCharmID, err - } - if !userSuppliedBase.Empty() && !userSuppliedBase.IsCompatible(baseToUse) { - return resourceIDs, apiCharmID, err - } - resolvedOrigin.Base = baseToUse - series, err := corebase.GetSeriesFromBase(baseToUse) - if err != nil { - return resourceIDs, apiCharmID, err - } - resolvedURL = resolvedURL.WithSeries(series) - // Add charm expects base or series, one of them should exist. - resultOrigin, err := charmsAPIClient.AddCharm(resolvedURL, resolvedOrigin, false) - if err != nil { - return resourceIDs, apiCharmID, err - } - resourceIDs, err = c.getResourceIDs(transformedInput, conn, deployInfo, pendingResources) - if err != nil { - return resourceIDs, apiCharmID, err - } + fileSystem := osFilesystem{} + // Upload the provided local resources to Juju + uploadErr := uploadExistingPendingResources(deployInfo.Name, localPendingResources, fileSystem, resourceAPIClient) - apiCharmID = apiapplication.CharmID{ - URL: resolvedURL.String(), - Origin: resultOrigin, + if uploadErr != nil { + return uploadErr } + return nil - return resourceIDs, apiCharmID, nil } // TODO (hml) 23-Feb-2024 @@ -469,7 +423,54 @@ func (c applicationsClient) legacyDeploy(ctx context.Context, conn api.Connectio charmsAPIClient := apicharms.NewClient(conn) modelconfigAPIClient := apimodelconfig.NewClient(conn) - resolvedURL, resolvedOrigin, supportedBases, err := getCharmResolvedUrlAndOrigin(conn, transformedInput) + channel, err := charm.ParseChannel(transformedInput.charmChannel) + if err != nil { + return err + } + + charmURL, err := resolveCharmURL(transformedInput.charmName) + if err != nil { + return err + } + + if charmURL.Revision != UnspecifiedRevision { + return fmt.Errorf("cannot specify revision in a charm name") + } + if transformedInput.charmRevision != UnspecifiedRevision && channel.Empty() { + return fmt.Errorf("specifying a revision requires a channel for future upgrades") + } + + userSuppliedBase := transformedInput.charmBase + platformCons, err := modelconfigAPIClient.GetModelConstraints() + if err != nil { + return err + } + platform := utils.MakePlatform(transformedInput.constraints, userSuppliedBase, platformCons) + + urlForOrigin := charmURL + if transformedInput.charmRevision != UnspecifiedRevision { + urlForOrigin = urlForOrigin.WithRevision(transformedInput.charmRevision) + } + + // Juju 2.9 cares that the series is in the origin. Juju 3.3 does not. + // We are supporting both now. + if !userSuppliedBase.Empty() { + userSuppliedSeries, err := corebase.GetSeriesFromBase(userSuppliedBase) + if err != nil { + return err + } + urlForOrigin = urlForOrigin.WithSeries(userSuppliedSeries) + } + + origin, err := utils.MakeOrigin(charm.Schema(urlForOrigin.Schema), transformedInput.charmRevision, channel, platform) + if err != nil { + return err + } + + // Charm or bundle has been supplied as a URL so we resolve and + // deploy using the store but pass in the origin command line + // argument so users can target a specific origin. + resolvedURL, resolvedOrigin, supportedBases, err := resolveCharm(charmsAPIClient, charmURL, origin) if err != nil { return err } @@ -477,7 +478,7 @@ func (c applicationsClient) legacyDeploy(ctx context.Context, conn api.Connectio return jujuerrors.NotSupportedf("deploying bundles") } c.Tracef("resolveCharm returned", map[string]interface{}{"resolvedURL": resolvedURL, "resolvedOrigin": resolvedOrigin, "supportedBases": supportedBases}) - userSuppliedBase := transformedInput.charmBase + baseToUse, err := c.baseToUse(modelconfigAPIClient, userSuppliedBase, resolvedOrigin.Base, supportedBases) if err != nil { c.Warnf("failed to get a suggested operating system from resolved charm response", map[string]interface{}{"err": err}) @@ -1459,7 +1460,7 @@ func (c applicationsClient) updateResources(appName string, resources map[string } func addPendingResources(appName string, charmResourcesToAdd map[string]charmresources.Meta, resourcesToUse map[string]string, - charmID apiapplication.CharmID, resourcesAPIClient ResourceAPIClient) (map[string]string, error) { + charmID apiapplication.CharmID, resourceAPIClient ResourceAPIClient) (map[string]string, error) { pendingResourcesforAdd := []charmresources.Resource{} resourceIDs := map[string]string{} @@ -1508,7 +1509,7 @@ func addPendingResources(appName string, charmResourcesToAdd map[string]charmres if openResErr != nil { return nil, typedError(openResErr) } - toRequestUpload, err := resourcesAPIClient.UploadPendingResource(appName, localResource, deployValue, r) + toRequestUpload, err := resourceAPIClient.UploadPendingResource(appName, localResource, deployValue, r) if err != nil { return nil, typedError(err) } @@ -1528,7 +1529,7 @@ func addPendingResources(appName string, charmResourcesToAdd map[string]charmres }, Resources: pendingResourcesforAdd, } - toRequestAdd, err := resourcesAPIClient.AddPendingResources(resourcesReqforAdd) + toRequestAdd, err := resourceAPIClient.AddPendingResources(resourcesReqforAdd) if err != nil { return nil, typedError(err) } diff --git a/internal/juju/interfaces.go b/internal/juju/interfaces.go index c7ea4bd3..1447f4d3 100644 --- a/internal/juju/interfaces.go +++ b/internal/juju/interfaces.go @@ -46,6 +46,7 @@ type ApplicationAPIClient interface { ApplicationsInfo(applications []names.ApplicationTag) ([]params.ApplicationInfoResult, error) Deploy(args apiapplication.DeployArgs) error DestroyUnits(in apiapplication.DestroyUnitsParams) ([]params.DestroyUnitResult, error) + DeployFromRepository(arg apiapplication.DeployFromRepositoryArg) (apiapplication.DeployInfo, []apiapplication.PendingResourceUpload, []error) DestroyApplications(in apiapplication.DestroyApplicationsParams) ([]params.DestroyApplicationResult, error) Expose(application string, exposedEndpoints map[string]params.ExposedEndpoint) error Get(branchName, application string) (*params.ApplicationGetResults, error) @@ -66,6 +67,7 @@ type ModelConfigAPIClient interface { type ResourceAPIClient interface { AddPendingResources(args apiresources.AddPendingResourcesArgs) ([]string, error) ListResources(applications []string) ([]resources.ApplicationResources, error) + Upload(application, name, filename, pendingID string, reader io.ReadSeeker) error UploadPendingResource(applicationID string, resource charmresources.Resource, filename string, r io.ReadSeeker) (id string, err error) } diff --git a/internal/juju/mock_test.go b/internal/juju/mock_test.go index 47212f2e..e521534a 100644 --- a/internal/juju/mock_test.go +++ b/internal/juju/mock_test.go @@ -304,6 +304,22 @@ func (mr *MockApplicationAPIClientMockRecorder) Deploy(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deploy", reflect.TypeOf((*MockApplicationAPIClient)(nil).Deploy), arg0) } +// DeployFromRepository mocks base method. +func (m *MockApplicationAPIClient) DeployFromRepository(arg0 application.DeployFromRepositoryArg) (application.DeployInfo, []application.PendingResourceUpload, []error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeployFromRepository", arg0) + ret0, _ := ret[0].(application.DeployInfo) + ret1, _ := ret[1].([]application.PendingResourceUpload) + ret2, _ := ret[2].([]error) + return ret0, ret1, ret2 +} + +// DeployFromRepository indicates an expected call of DeployFromRepository. +func (mr *MockApplicationAPIClientMockRecorder) DeployFromRepository(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeployFromRepository", reflect.TypeOf((*MockApplicationAPIClient)(nil).DeployFromRepository), arg0) +} + // DestroyApplications mocks base method. func (m *MockApplicationAPIClient) DestroyApplications(arg0 application.DestroyApplicationsParams) ([]params.DestroyApplicationResult, error) { m.ctrl.T.Helper() @@ -574,6 +590,20 @@ func (mr *MockResourceAPIClientMockRecorder) ListResources(arg0 any) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListResources", reflect.TypeOf((*MockResourceAPIClient)(nil).ListResources), arg0) } +// Upload mocks base method. +func (m *MockResourceAPIClient) Upload(arg0, arg1, arg2, arg3 string, arg4 io.ReadSeeker) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Upload", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(error) + return ret0 +} + +// Upload indicates an expected call of Upload. +func (mr *MockResourceAPIClientMockRecorder) Upload(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upload", reflect.TypeOf((*MockResourceAPIClient)(nil).Upload), arg0, arg1, arg2, arg3, arg4) +} + // UploadPendingResource mocks base method. func (m *MockResourceAPIClient) UploadPendingResource(arg0 string, arg1 resource.Resource, arg2 string, arg3 io.ReadSeeker) (string, error) { m.ctrl.T.Helper() diff --git a/internal/juju/resources.go b/internal/juju/resources.go index 550f58d0..dc8eaaa5 100644 --- a/internal/juju/resources.go +++ b/internal/juju/resources.go @@ -4,152 +4,65 @@ package juju import ( - "fmt" + "os" - "github.com/juju/charm/v12" charmresources "github.com/juju/charm/v12/resource" - "github.com/juju/juju/api" + jujuerrors "github.com/juju/errors" apiapplication "github.com/juju/juju/api/client/application" - apicharms "github.com/juju/juju/api/client/charms" - apimodelconfig "github.com/juju/juju/api/client/modelconfig" - apicommoncharm "github.com/juju/juju/api/common/charm" - "github.com/juju/juju/cmd/juju/application/utils" resourcecmd "github.com/juju/juju/cmd/juju/resource" - corebase "github.com/juju/juju/core/base" + "github.com/juju/juju/cmd/modelcmd" ) -// getResourceIDs uploads pending resources and -// returns the resource IDs of uploaded resources -func (c applicationsClient) getResourceIDs(transformedInput transformedCreateApplicationInput, conn api.Connection, deployInfo apiapplication.DeployInfo, pendingResources []apiapplication.PendingResourceUpload) (map[string]string, error) { - resourceIDs := map[string]string{} - charmsAPIClient := apicharms.NewClient(conn) - modelconfigAPIClient := apimodelconfig.NewClient(conn) - resourcesAPIClient, err := c.getResourceAPIClient(conn) - if err != nil { - return resourceIDs, err - } - resolvedURL, resolvedOrigin, supportedBases, err := getCharmResolvedUrlAndOrigin(conn, transformedInput) - if err != nil { - return resourceIDs, err - } - - userSuppliedBase := transformedInput.charmBase - baseToUse, err := c.baseToUse(modelconfigAPIClient, userSuppliedBase, resolvedOrigin.Base, supportedBases) - if err != nil { - return resourceIDs, err - } - resolvedOrigin.Base = baseToUse - // 3.3 version of ResolveCharm does not always include the series - // in the url. However, juju 2.9 requires it. - series, err := corebase.GetSeriesFromBase(baseToUse) - if err != nil { - return resourceIDs, err - } - resolvedURL = resolvedURL.WithSeries(series) - - resultOrigin, err := charmsAPIClient.AddCharm(resolvedURL, resolvedOrigin, false) - if err != nil { - return resourceIDs, err - } - charmID := apiapplication.CharmID{ - URL: resolvedURL.String(), - Origin: resultOrigin, - } +type osFilesystem struct{} - charmInfo, err := charmsAPIClient.CharmInfo(charmID.URL) - if err != nil { - return resourceIDs, err - } - - for _, resourceMeta := range charmInfo.Meta.Resources { - for _, pendingResource := range pendingResources { - if pendingResource.Name == resourceMeta.Name { - fileSystem := osFilesystem{} - localResource := charmresources.Resource{ - Meta: resourceMeta, - Origin: charmresources.OriginStore, - } - t, typeParseErr := charmresources.ParseType(resourceMeta.Type.String()) - if typeParseErr != nil { - return resourceIDs, typeParseErr - } - r, openResErr := resourcecmd.OpenResource(pendingResource.Filename, t, fileSystem.Open) - if openResErr != nil { - return resourceIDs, openResErr - } - toRequestUpload, err := resourcesAPIClient.UploadPendingResource(deployInfo.Name, localResource, pendingResource.Filename, r) - if err != nil { - return resourceIDs, err - } - resourceIDs[resourceMeta.Name] = toRequestUpload - } - } - } - return resourceIDs, nil +func (osFilesystem) Create(name string) (*os.File, error) { + return os.Create(name) } -// getResourceIDs uploads pending resources and -// returns the resource IDs of uploaded resources -func getCharmResolvedUrlAndOrigin(conn api.Connection, transformedInput transformedCreateApplicationInput) (*charm.URL, apicommoncharm.Origin, []corebase.Base, error) { - charmsAPIClient := apicharms.NewClient(conn) - modelconfigAPIClient := apimodelconfig.NewClient(conn) - - channel, err := charm.ParseChannel(transformedInput.charmChannel) - if err != nil { - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err - } - - charmURL, err := resolveCharmURL(transformedInput.charmName) - if err != nil { - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err - } +func (osFilesystem) RemoveAll(path string) error { + return os.RemoveAll(path) +} - if charmURL.Revision != UnspecifiedRevision { - err := fmt.Errorf("cannot specify revision in a charm name") - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err - } - if transformedInput.charmRevision != UnspecifiedRevision && channel.Empty() { - err = fmt.Errorf("specifying a revision requires a channel for future upgrades") - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err - } +func (osFilesystem) Open(name string) (modelcmd.ReadSeekCloser, error) { + return os.Open(name) +} - userSuppliedBase := transformedInput.charmBase - if err != nil { - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err - } - platformCons, err := modelconfigAPIClient.GetModelConstraints() - if err != nil { - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err - } - platform := utils.MakePlatform(transformedInput.constraints, userSuppliedBase, platformCons) +func (osFilesystem) OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) { + return os.OpenFile(name, flag, perm) +} - urlForOrigin := charmURL - if transformedInput.charmRevision != UnspecifiedRevision { - urlForOrigin = urlForOrigin.WithRevision(transformedInput.charmRevision) - } +func (osFilesystem) Stat(name string) (os.FileInfo, error) { + return os.Stat(name) +} - // Juju 2.9 cares that the series is in the origin. Juju 3.3 does not. - // We are supporting both now. - if !userSuppliedBase.Empty() { - userSuppliedSeries, err := corebase.GetSeriesFromBase(userSuppliedBase) - if err != nil { - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err +// UploadExistingPendingResources uploads local resources. Used +// after DeployFromRepository, where the resources have been added +// to the controller. +func uploadExistingPendingResources( + appName string, + pendingResources []apiapplication.PendingResourceUpload, + filesystem modelcmd.Filesystem, + resourceAPIClient ResourceAPIClient) error { + if pendingResources == nil { + return nil + } + + for _, pendingResUpload := range pendingResources { + t, typeParseErr := charmresources.ParseType(pendingResUpload.Type) + if typeParseErr != nil { + return jujuerrors.Annotatef(typeParseErr, "invalid type %v for pending resource %v", + pendingResUpload.Type, pendingResUpload.Name) } - urlForOrigin = urlForOrigin.WithSeries(userSuppliedSeries) - } - origin, err := utils.MakeOrigin(charm.Schema(urlForOrigin.Schema), transformedInput.charmRevision, channel, platform) - if err != nil { - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err - } + r, openResErr := resourcecmd.OpenResource(pendingResUpload.Filename, t, filesystem.Open) + if openResErr != nil { + return jujuerrors.Annotatef(openResErr, "unable to open resource %v", pendingResUpload.Name) + } + uploadErr := resourceAPIClient.Upload(appName, pendingResUpload.Name, pendingResUpload.Filename, "", r) - // Charm or bundle has been supplied as a URL, so we resolve and - // deploy using the store but pass in the origin command line - // argument so users can target a specific origin. - resolvedURL, resolvedOrigin, supportedBases, err := resolveCharm(charmsAPIClient, charmURL, origin) - if err != nil { - return nil, apicommoncharm.Origin{}, []corebase.Base{}, err + if uploadErr != nil { + return jujuerrors.Trace(uploadErr) + } } - - return resolvedURL, resolvedOrigin, supportedBases, nil + return nil } diff --git a/internal/provider/resource_application.go b/internal/provider/resource_application.go index cf67fb9e..936cd459 100644 --- a/internal/provider/resource_application.go +++ b/internal/provider/resource_application.go @@ -6,7 +6,6 @@ package provider import ( "context" "fmt" - "strconv" "strings" "github.com/dustin/go-humanize" @@ -98,13 +97,6 @@ type applicationResourceModel struct { ID types.String `tfsdk:"id"` } -// isInt checks if strings could be converted to an integer -// Used to detect resources which are given with revision number -func isInt(s string) bool { - _, err := strconv.Atoi(s) - return err == nil -} - func (r *applicationResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_application" } @@ -803,7 +795,7 @@ func (r *applicationResource) configureConfigData(ctx context.Context, configTyp // Add if the value has changed from the previous state if previousValue, found := previousConfig[k]; found { if !juju.EqualConfigEntries(v, previousValue) { - // remember that this terraform schema type only accepts strings + // remember that this Terraform schema type only accepts strings previousConfig[k] = v.String() changes = true } @@ -920,7 +912,7 @@ func (r *applicationResource) Update(ctx context.Context, req resource.UpdateReq } if !planCharm.Series.Equal(stateCharm.Series) || !planCharm.Base.Equal(stateCharm.Base) { - // This violates terraform's declarative model. We could implement + // This violates Terraform's declarative model. We could implement // `juju set-application-base`, usually used after `upgrade-machine`, // which would change the operating system used for future units of // the application provided the charm supported it, but not change @@ -1136,8 +1128,7 @@ func (r *applicationResource) updateStorage( func (r *applicationResource) computeExposeDeltas(ctx context.Context, stateExpose types.List, planExpose types.List) (map[string]interface{}, []string, diag.Diagnostics) { diags := diag.Diagnostics{} if planExpose.IsNull() { - // if plan is nil we unexpose everything via - // an non empty list. + // if plan is nil we unexpose everything via a non-empty list. return nil, []string{""}, diags } if stateExpose.IsNull() { diff --git a/internal/provider/resource_application_test.go b/internal/provider/resource_application_test.go index 664c3619..a1b8fe4a 100644 --- a/internal/provider/resource_application_test.go +++ b/internal/provider/resource_application_test.go @@ -348,6 +348,12 @@ func TestAcc_CustomResourcesAddedToPlanMicrok8s(t *testing.T) { if testingCloud != MicroK8sTesting { t.Skip(t.Name() + " only runs with Microk8s") } + agentVersion := os.Getenv(TestJujuAgentVersion) + if agentVersion == "" { + t.Skipf("%s is not set", TestJujuAgentVersion) + } else if internaltesting.CompareVersions(agentVersion, "3.0.3") < 0 { + t.Skipf("%s is not set or is below 3.0.3", TestJujuAgentVersion) + } modelName := acctest.RandomWithPrefix("tf-test-custom-resource-updates-microk8s") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -355,37 +361,37 @@ func TestAcc_CustomResourcesAddedToPlanMicrok8s(t *testing.T) { Steps: []resource.TestStep{ { // deploy charm without custom resource - Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.3/edge"), + Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.0/stable"), Check: resource.ComposeTestCheckFunc( resource.TestCheckNoResourceAttr("juju_application.this", "resources"), ), }, { // Add a custom resource - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/edge", "ausf-image", "gatici/sdcore-ausf:1.4"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/stable", "grafana-image", "gatici/grafana:10"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "gatici/sdcore-ausf:1.4"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "gatici/grafana:10"), ), ExpectNonEmptyPlan: true, }, { // Add another custom resource - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/edge", "ausf-image", "gatici/sdcore-ausf:latest"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/stable", "grafana-image", "gatici/grafana:9"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "gatici/sdcore-ausf:latest"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "gatici/grafana:9"), ), ExpectNonEmptyPlan: true, }, { // Add resource revision - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/edge", "ausf-image", "30"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/stable", "grafana-image", "61"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "30"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "61"), ), }, { // Remove resource revision - Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.3/edge"), + Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.0/stable"), Check: resource.ComposeTestCheckFunc( resource.TestCheckNoResourceAttr("juju_application.this", "resources"), ), @@ -398,6 +404,12 @@ func TestAcc_CustomResourceUpdatesMicrok8s(t *testing.T) { if testingCloud != MicroK8sTesting { t.Skip(t.Name() + " only runs with Microk8s") } + agentVersion := os.Getenv(TestJujuAgentVersion) + if agentVersion == "" { + t.Skipf("%s is not set", TestJujuAgentVersion) + } else if internaltesting.CompareVersions(agentVersion, "3.0.3") < 0 { + t.Skipf("%s is not set or is below 3.0.3", TestJujuAgentVersion) + } modelName := acctest.RandomWithPrefix("tf-test-custom-resource-updates-microk8s") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -405,37 +417,37 @@ func TestAcc_CustomResourceUpdatesMicrok8s(t *testing.T) { Steps: []resource.TestStep{ { // Deploy charm with a custom resource - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/beta", "ausf-image", "gatici/sdcore-ausf:latest"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/edge", "grafana-image", "gatici/grafana:9"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "gatici/sdcore-ausf:latest"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "gatici/grafana:9"), ), ExpectNonEmptyPlan: true, }, { // Keep charm channel and update resource to another custom image - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/beta", "ausf-image", "gatici/sdcore-ausf:1.4"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/edge", "grafana-image", "gatici/grafana:10"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "gatici/sdcore-ausf:1.4"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "gatici/grafana:10"), ), ExpectNonEmptyPlan: true, }, { // Update charm channel and update resource to a revision - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/edge", "ausf-image", "10"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/stable", "grafana-image", "59"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "10"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "59"), ), }, { // Update charm channel and keep resource revision - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/beta", "ausf-image", "10"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/beta", "grafana-image", "59"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "10"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "59"), ), }, { // Keep charm channel and remove resource revision - Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.3/beta"), + Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.0/beta"), Check: resource.ComposeTestCheckFunc( resource.TestCheckNoResourceAttr("juju_application.this", "resources"), ), @@ -448,6 +460,12 @@ func TestAcc_CustomResourcesRemovedFromPlanMicrok8s(t *testing.T) { if testingCloud != MicroK8sTesting { t.Skip(t.Name() + " only runs with Microk8s") } + agentVersion := os.Getenv(TestJujuAgentVersion) + if agentVersion == "" { + t.Skipf("%s is not set", TestJujuAgentVersion) + } else if internaltesting.CompareVersions(agentVersion, "3.0.3") < 0 { + t.Skipf("%s is not set or is below 3.0.3", TestJujuAgentVersion) + } modelName := acctest.RandomWithPrefix("tf-test-custom-resource-updates-microk8s") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -455,50 +473,36 @@ func TestAcc_CustomResourcesRemovedFromPlanMicrok8s(t *testing.T) { Steps: []resource.TestStep{ { // Deploy charm with a custom resource - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/edge", "ausf-image", "gatici/sdcore-ausf:latest"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/edge", "grafana-image", "gatici/grafana:9"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "gatici/sdcore-ausf:latest"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "gatici/grafana:9"), ), ExpectNonEmptyPlan: true, }, { // Keep charm channel and remove custom resource - Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.3/edge"), + Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.0/edge"), Check: resource.ComposeTestCheckFunc( resource.TestCheckNoResourceAttr("juju_application.this", "resources"), ), }, { // Keep charm channel and add resource revision - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/edge", "ausf-image", "30"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/edge", "grafana-image", "60"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "30"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "60"), ), }, { // Update charm channel and keep resource revision - Config: testAccResourceApplicationWithCustomResources(modelName, "1.3/beta", "ausf-image", "30"), + Config: testAccResourceApplicationWithCustomResources(modelName, "1.0/stable", "grafana-image", "60"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("juju_application.this", "resources.ausf-image", "30"), + resource.TestCheckResourceAttr("juju_application.this", "resources.grafana-image", "60"), ), }, { // Update charm channel and remove resource revision - Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.3/edge"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckNoResourceAttr("juju_application.this", "resources"), - ), - }, - { - // Update charm channel and remove image resource which is given in a json file - Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.3/beta"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckNoResourceAttr("juju_application.this", "resources"), - ), - }, - { - // Update charm channel and remove resource - Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.3/edge"), + Config: testAccResourceApplicationWithoutCustomResources(modelName, "1.0/beta"), Check: resource.ComposeTestCheckFunc( resource.TestCheckNoResourceAttr("juju_application.this", "resources"), ), @@ -867,7 +871,7 @@ resource "juju_application" "this" { model = juju_model.this.name name = "test-app" charm { - name = "sdcore-ausf-k8s" + name = "grafana-k8s" channel = "%s" } trust = true