Skip to content

Commit

Permalink
fix(application): handle storage details conversion error
Browse files Browse the repository at this point in the history
- Added error handling for "cannot convert storage details"
  in `applicationsClient.ReadApplication`.
- Improved retry logic for storage readiness in `ReadApplicationWithRetryOnNotFound`.

This commit ensures that the application correctly handles storage conversion errors and retries
appropriately until the storage is ready.
  • Loading branch information
anvial committed Jul 12, 2024
1 parent 1fb72cf commit 185ce0d
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 15 deletions.
27 changes: 15 additions & 12 deletions internal/juju/applications.go
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,18 @@ func (c applicationsClient) ReadApplicationWithRetryOnNotFound(ctx context.Conte
c.Errorf(err, fmt.Sprintf("ReadApplicationWithRetryOnNotFound %q", input.AppName))
return nil
}

// NOTE: Applications can always have storage. However, they
// will not be listed right after the application is created. So
// we need to wait for the storage to be ready. And we need to
// check if all storage constraints have pool equal "" and size equal 0
// to drop the error.
for _, storage := range output.Storage {
if storage.Pool == "" || storage.Size == 0 {
return fmt.Errorf("ReadApplicationWithRetryOnNotFound: no storage found in output")
}
}

// NOTE: An IAAS subordinate should also have machines. However, they
// will not be listed until after the relation has been created.
// Those happen with the integration resource which will not be
Expand All @@ -779,17 +791,6 @@ func (c applicationsClient) ReadApplicationWithRetryOnNotFound(ctx context.Conte
return fmt.Errorf("ReadApplicationWithRetryOnNotFound: need %d machines, have %d", output.Units, len(machines))
}

// NOTE: Applications can always have storage. However, they
// will not be listed right after the application is created. So
// we need to wait for the storage to be ready. And we need to
// check if all storage constraints have pool equal "" and size equal 0
// to drop the error.
for _, storage := range output.Storage {
if storage.Pool == "" || storage.Size == 0 {
return fmt.Errorf("ReadApplicationWithRetryOnNotFound: no storage found in output")
}
}

// NOTE: An IAAS subordinate should also have machines. However, they
// will not be listed until after the relation has been created.
// Those happen with the integration resource which will not be
Expand Down Expand Up @@ -899,7 +900,9 @@ func (c applicationsClient) ReadApplication(input *ReadApplicationInput) (*ReadA
IncludeStorage: true,
})
if err != nil {
if strings.Contains(err.Error(), "filesystem for storage instance") || strings.Contains(err.Error(), "volume for storage instance") {
if strings.Contains(err.Error(), "filesystem for storage instance") ||
strings.Contains(err.Error(), "volume for storage instance") ||
strings.Contains(err.Error(), "cannot convert storage details") {
// Retry if we get this error. It means the storage is not ready yet.
return nil, &storageNotFoundError{input.AppName}
}
Expand Down
61 changes: 58 additions & 3 deletions internal/provider/resource_application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ func TestAcc_ResourceApplication_UpdateEndpointBindings(t *testing.T) {
})
}

func TestAcc_ResourceApplication_Storage(t *testing.T) {
func TestAcc_ResourceApplication_StorageLXD(t *testing.T) {
if testingCloud != LXDCloudTesting {
t.Skip(t.Name() + " only runs with LXD")
}
Expand All @@ -589,7 +589,7 @@ func TestAcc_ResourceApplication_Storage(t *testing.T) {
ProtoV6ProviderFactories: frameworkProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccResourceApplicationStorage(modelName, appName, storageConstraints),
Config: testAccResourceApplicationStorageLXD(modelName, appName, storageConstraints),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("juju_application."+appName, "model", modelName),
resource.TestCheckResourceAttr("juju_application."+appName, "storage_directives.runner", "2G"),
Expand All @@ -603,6 +603,34 @@ func TestAcc_ResourceApplication_Storage(t *testing.T) {
})
}

func TestAcc_ResourceApplication_StorageK8s(t *testing.T) {
if testingCloud != MicroK8sTesting {
t.Skip(t.Name() + " only runs with Microk8s")
}
modelName := acctest.RandomWithPrefix("tf-test-application-storage")
appName := "test-app-storage"

storageConstraints := map[string]string{"label": "pgdata", "size": "2G"}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: frameworkProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccResourceApplicationStorageK8s(modelName, appName, storageConstraints),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("juju_application."+appName, "model", modelName),
resource.TestCheckResourceAttr("juju_application."+appName, "storage_directives.pgdata", "2G"),
resource.TestCheckResourceAttr("juju_application."+appName, "storage.0.label", "pgdata"),
resource.TestCheckResourceAttr("juju_application."+appName, "storage.0.count", "1"),
resource.TestCheckResourceAttr("juju_application."+appName, "storage.0.size", "2G"),
resource.TestCheckResourceAttr("juju_application."+appName, "storage.0.pool", "kubernetes"),
),
},
},
})
}

func testAccResourceApplicationBasic_Minimal(modelName, charmName string) string {
return fmt.Sprintf(`
resource "juju_model" "testmodel" {
Expand Down Expand Up @@ -950,7 +978,7 @@ resource "juju_application" "{{.AppName}}" {
})
}

func testAccResourceApplicationStorage(modelName, appName string, storageConstraints map[string]string) string {
func testAccResourceApplicationStorageLXD(modelName, appName string, storageConstraints map[string]string) string {
return internaltesting.GetStringFromTemplateWithData("testAccResourceApplicationStorage", `
resource "juju_model" "{{.ModelName}}" {
name = "{{.ModelName}}"
Expand Down Expand Up @@ -978,6 +1006,33 @@ resource "juju_application" "{{.AppName}}" {
})
}

func testAccResourceApplicationStorageK8s(modelName, appName string, storageConstraints map[string]string) string {
return internaltesting.GetStringFromTemplateWithData("testAccResourceApplicationStorage", `
resource "juju_model" "{{.ModelName}}" {
name = "{{.ModelName}}"
}
resource "juju_application" "{{.AppName}}" {
model = juju_model.{{.ModelName}}.name
name = "{{.AppName}}"
charm {
name = "postgresql-k8s"
channel = "14/stable"
}
storage_directives = {
{{.StorageConstraints.label}} = "{{.StorageConstraints.size}}"
}
units = 1
}
`, internaltesting.TemplateData{
"ModelName": modelName,
"AppName": appName,
"StorageConstraints": storageConstraints,
})
}

func testCheckEndpointsAreSetToCorrectSpace(modelName, appName, defaultSpace string, configuredEndpoints map[string]string) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn, err := TestClient.Models.GetConnection(&modelName)
Expand Down

0 comments on commit 185ce0d

Please sign in to comment.