feat(provider): Support worker filter on dynamic host catalogs

This commit is contained in:
Hugo committed 2024-10-21 17:15:10 +01:00
1 parent ad43c0bec0
commit f9fd811205
6 files changed
+302 -161

No files matched your search

@@ -118,6 +118,11 @@ func resourceHostCatalogPlugin() *schema.Resource {
Optional: true,
Computed: true,
},
WorkerFilterKey: {
Description: "HCP Only. A filter used to control which PKI workers can handle dynamic host catalog requests.",
Type: schema.TypeString,
Optional: true,
},
internalSecretsConfigHmacKey: {
Description: "Internal only. HMAC of (serverSecretsHmac + config secrets). Used for proper secrets handling.",
Type: schema.TypeString,
@@ -294,6 +299,9 @@ func setFromHostCatalogPluginResponseMap(d *schema.ResourceData, raw map[string]
if err := d.Set(ScopeIdKey, raw[ScopeIdKey]); err != nil {
return err
}
if err := d.Set(WorkerFilterKey, raw[WorkerFilterKey]); err != nil {
return err
}
// Plugin stuff
{
if err := d.Set(PluginIdKey, raw[PluginIdKey]); err != nil {
@@ -399,6 +407,12 @@ func resourceHostCatalogPluginCreate(ctx context.Context, d *schema.ResourceData
opts = append(opts, hostcatalogs.WithDescription(descStr))
}
workerFilterVal, ok := d.GetOk(WorkerFilterKey)
if ok {
workerFilterStr := workerFilterVal.(string)
opts = append(opts, hostcatalogs.WithWorkerFilter(workerFilterStr))
}
attrsVal, ok := d.GetOk(AttributesJsonKey)
if ok {
attrsStr, err := parseutil.ParsePath(attrsVal.(string))
@@ -556,6 +570,15 @@ func resourceHostCatalogPluginUpdate(ctx context.Context, d *schema.ResourceData
}
}
if d.HasChange(WorkerFilterKey) {
opts = append(opts, hostcatalogs.DefaultWorkerFilter())
workerFilterVal, ok := d.GetOk(WorkerFilterKey)
if ok {
workerFilterStr := workerFilterVal.(string)
opts = append(opts, hostcatalogs.WithWorkerFilter(workerFilterStr))
}
}
if d.HasChange(AttributesJsonKey) {
attrsVal, ok := d.GetOk(AttributesJsonKey)
if ok {
@@ -22,6 +22,9 @@ const (
testPluginHostCatalogDescription = "bar"
testPluginHostCatalogDescriptionUpdate = "bar foo"
testPluginHostCatalogDescriptionUpdate2 = "bar foo foo"
testPluginHostCatalogWorkerFilterUpdate = "\"dev\" in \"/tags/type\""
testPluginHostCatalogWorkerFilterUpdate2 = "\"pki\" in \"/tags/type\""
)
// expectedAttributesState is used here and in plugin host sets to control how
@@ -244,6 +247,212 @@ func TestAccPluginHostCatalog(t *testing.T) {
})
}
func TestAccPluginHostCatalogWithWorkerFilter(t *testing.T) {
t.Skip("Skipping test until Boundary Terraform Provider can unit tests for Boundary Enterprise only features")
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
resName := "boundary_host_catalog_plugin.foo"
initialValuesStr := fmt.Sprintf(`
description = "%s"
attributes_json = jsonencode({
foo = "bar"
zip = "zap"
})
secrets_json = jsonencode({
hush = "puppies"
})
`,
testPluginHostCatalogDescription,
)
// Changed description, secrets and added worker filter.
valuesStrUpdate1 := fmt.Sprintf(`
description = "%s"
worker_filter = %q
attributes_json = jsonencode({
foo = "bar"
zip = "zoop"
})
secrets_json = jsonencode({
flush = "fluppies"
})
`,
testPluginHostCatalogDescriptionUpdate,
testPluginHostCatalogWorkerFilterUpdate,
)
// Changed description, no secrets update, nullify worker filter.
valuesStrUpdate2 := fmt.Sprintf(`
description = "%s"
worker_filter = null
attributes_json = jsonencode({
foo = "bar"
zip = "zoop"
})
secrets_json = jsonencode({
flush = "fluppies"
})
`,
testPluginHostCatalogDescriptionUpdate2,
)
// Same description and set a worker filter again, now empty secrets.
valuesStrUpdate3 := fmt.Sprintf(`
description = "%s"
worker_filter = %q
attributes_json = jsonencode({
foo = "bar"
zip = "zoop"
})
`,
testPluginHostCatalogDescriptionUpdate2,
testPluginHostCatalogWorkerFilterUpdate2,
)
// Same description, now explicitly unset secrets and blankify attrs and
// worker filter.
valuesStrUpdate4 := fmt.Sprintf(`
description = "%s"
secrets_json = "null"
`,
testPluginHostCatalogDescriptionUpdate2,
)
// Set values again
valuesStrUpdate5 := fmt.Sprintf(`
description = "%s"
worker_filter = %q
attributes_json = jsonencode({
foo = "bar"
zip = "zoop"
})
secrets_json = jsonencode({
flush = "fluppies"
})
`,
testPluginHostCatalogDescriptionUpdate2,
testPluginHostCatalogWorkerFilterUpdate2,
)
// Explicitly set both secrets and attributes to null
valuesStrUpdate6 := fmt.Sprintf(`
description = "%s"
worker_filter = %q
attributes_json = "null"
secrets_json = "null"
`,
testPluginHostCatalogDescriptionUpdate2,
testPluginHostCatalogWorkerFilterUpdate2,
)
initialHcl := fmt.Sprintf(projPluginHostCatalogBase, initialValuesStr)
update1Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate1)
update2Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate2)
update3Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate3)
update4Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate4)
update5Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate5)
update6Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate6)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckHostCatalogResourceDestroy(t, provider, baseHostCatalogType),
Steps: []resource.TestStep{
{
// test create
Config: testConfig(url, fooOrg, firstProjectFoo, initialHcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckScopeResourceExists(provider, "boundary_scope.org1"),
testAccCheckScopeResourceExists(provider, "boundary_scope.proj1"),
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslyEmptyNowSet),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescription),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update1Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetButChanged),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate),
resource.TestCheckResourceAttr(resName, WorkerFilterKey, testPluginHostCatalogWorkerFilterUpdate),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update2Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNoChange),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// this runs the same HCL; mostly used in some manual checking
// to ensure update is still called even when nothing has
// changed (as we need for secrets)
Config: testConfig(url, fooOrg, firstProjectFoo, update2Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNoChange),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update3Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNoChange),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
resource.TestCheckResourceAttr(resName, WorkerFilterKey, testPluginHostCatalogWorkerFilterUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update4Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNowEmpty),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update5Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslyEmptyNowSet),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
resource.TestCheckResourceAttr(resName, WorkerFilterKey, testPluginHostCatalogWorkerFilterUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update6Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNowEmpty),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
resource.TestCheckResourceAttr(resName, WorkerFilterKey, testPluginHostCatalogWorkerFilterUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
},
})
}
func testAccCheckPluginHostCatalogResourceExists(testProvider *schema.Provider, name string, expAttrState expectedAttributesState) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]