diff --git a/go.mod b/go.mod index fc31041ce..3d081e3fc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/outscale/terraform-provider-outscale -go 1.22.0 +go 1.23.0 require ( github.com/aws/aws-sdk-go v1.55.5 @@ -13,7 +13,7 @@ require ( github.com/hashicorp/terraform-plugin-mux v0.12.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 github.com/nav-inc/datetime v0.1.3 - github.com/outscale/osc-sdk-go/v2 v2.23.0 + github.com/outscale/osc-sdk-go/v2 v2.24.0 github.com/spf13/cast v1.6.0 ) @@ -58,7 +58,7 @@ require ( golang.org/x/crypto v0.23.0 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index d377af8e6..30f870b3a 100644 --- a/go.sum +++ b/go.sum @@ -142,8 +142,8 @@ github.com/nav-inc/datetime v0.1.3 h1:PaybPUsScX+Cd3TEa1tYpfwU61deCEhMTlCO2hONm1 github.com/nav-inc/datetime v0.1.3/go.mod h1:gKGf5G+cW7qkTo5TC/sieNyz6lYdrA9cf1PNV+pXIOE= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/outscale/osc-sdk-go/v2 v2.23.0 h1:Ib134+ThQHmMtURcMTD9eTiAKeF1gSufbH3RdYa9+bY= -github.com/outscale/osc-sdk-go/v2 v2.23.0/go.mod h1:kzhtUErCzKYl87bZ+kDMphDafmnwbsyFJY9iHF7NgNE= +github.com/outscale/osc-sdk-go/v2 v2.24.0 h1:4M0gJgYRKJQhIo5oZbiEbfuBv/Ls6iHaUyrEiBAbjYM= +github.com/outscale/osc-sdk-go/v2 v2.24.0/go.mod h1:kzhtUErCzKYl87bZ+kDMphDafmnwbsyFJY9iHF7NgNE= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -197,8 +197,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/outscale/data_source_outscale_policies.go b/outscale/data_source_outscale_policies.go new file mode 100644 index 000000000..e38873bd1 --- /dev/null +++ b/outscale/data_source_outscale_policies.go @@ -0,0 +1,143 @@ +package outscale + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" + "github.com/spf13/cast" +) + +func DataSourcePolicies() *schema.Resource { + return &schema.Resource{ + Read: DataSourcePoliciesRead, + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + "policies": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + + "policy_orn": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "resources_count": { + Type: schema.TypeInt, + Computed: true, + }, + "policy_default_version_id": { + Type: schema.TypeString, + Computed: true, + }, + "is_linkable": { + Type: schema.TypeBool, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourcePoliciesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + + filters, filtersOk := d.GetOk("filter") + if !filtersOk { + return fmt.Errorf("One of filters, only_linked, path_refix,... must be assigned") + } + filterReq := buildPoliciesFilters(filters.(*schema.Set)) + req := oscgo.NewReadPoliciesRequest() + req.SetFilters(*filterReq) + var resp oscgo.ReadPoliciesResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicies(context.Background()).ReadPoliciesRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetPoliciesOk(); !ok { + return fmt.Errorf("Unable to find Policies") + } + d.SetId(resource.UniqueId()) + policyResp := resp.GetPolicies() + policies := make([]map[string]interface{}, len(policyResp)) + + for i, v := range policyResp { + policy := make(map[string]interface{}) + policy["policy_name"] = v.GetPolicyName() + policy["policy_id"] = v.GetPolicyId() + policy["path"] = v.GetPath() + policy["orn"] = v.GetOrn() + policy["resources_count"] = v.GetResourcesCount() + policy["is_linkable"] = v.GetIsLinkable() + policy["policy_default_version_id"] = v.GetPolicyDefaultVersionId() + policy["description"] = v.GetDescription() + policy["creation_date"] = v.GetCreationDate() + policy["last_modification_date"] = v.GetLastModificationDate() + policies[i] = policy + } + return d.Set("policies", policies) +} + +func buildPoliciesFilters(set *schema.Set) *oscgo.ReadPoliciesFilters { + var filters oscgo.ReadPoliciesFilters + for _, v := range set.List() { + m := v.(map[string]interface{}) + var filterValues []string + for _, e := range m["values"].([]interface{}) { + filterValues = append(filterValues, e.(string)) + } + + switch name := m["name"].(string); name { + case "only_linked": + filters.SetOnlyLinked(cast.ToBool(filterValues[0])) + case "path_prefix": + filters.SetPathPrefix(filterValues[0]) + case "scope": + filters.SetScope(filterValues[0]) + default: + log.Printf("[Debug] Unknown Filter Name: %s.", name) + } + } + return &filters +} diff --git a/outscale/data_source_outscale_policy.go b/outscale/data_source_outscale_policy.go new file mode 100644 index 000000000..7863db868 --- /dev/null +++ b/outscale/data_source_outscale_policy.go @@ -0,0 +1,124 @@ +package outscale + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourcePolicy() *schema.Resource { + return &schema.Resource{ + Read: DataSourcePolicyRead, + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + }, + "policy_name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "document": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "resources_count": { + Type: schema.TypeInt, + Computed: true, + }, + "policy_default_version_id": { + Type: schema.TypeString, + Computed: true, + }, + "is_linkable": { + Type: schema.TypeBool, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DataSourcePolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadPolicyRequest(d.Get("policy_orn").(string)) + + var resp oscgo.ReadPolicyResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicy(context.Background()).ReadPolicyRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetPolicyOk(); !ok { + d.SetId("") + return nil + } + policy := resp.GetPolicy() + d.SetId(resource.UniqueId()) + if err := d.Set("policy_name", policy.GetPolicyName()); err != nil { + return err + } + if err := d.Set("policy_id", policy.GetPolicyId()); err != nil { + return err + } + if err := d.Set("path", policy.GetPath()); err != nil { + return err + } + if err := d.Set("orn", policy.GetOrn()); err != nil { + return err + } + if err := d.Set("resources_count", policy.GetResourcesCount()); err != nil { + return err + } + if err := d.Set("is_linkable", policy.GetIsLinkable()); err != nil { + return err + } + if err := d.Set("policy_default_version_id", policy.GetPolicyDefaultVersionId()); err != nil { + return err + } + if err := d.Set("description", policy.GetDescription()); err != nil { + return err + } + if err := d.Set("creation_date", (policy.GetCreationDate())); err != nil { + return err + } + if err := d.Set("last_modification_date", (policy.GetLastModificationDate())); err != nil { + return err + } + return nil +} diff --git a/outscale/data_source_outscale_policy_test.go b/outscale/data_source_outscale_policy_test.go new file mode 100644 index 000000000..b7e53f011 --- /dev/null +++ b/outscale/data_source_outscale_policy_test.go @@ -0,0 +1,37 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_data_policy_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_policy.data_test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPolicyDataConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "policy_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + ), + }, + }, + }) +} + +const testAccPolicyDataConfig = ` + resource "outscale_policy" "data_policy" { + policy_name = "TestACC_resoucePolicy" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/" + } + data "outscale_policy" "data_test" { + policy_orn = outscale_policy.data_policy.orn + } +` diff --git a/outscale/data_source_outscale_user.go b/outscale/data_source_outscale_user.go new file mode 100644 index 000000000..d1bb13e0e --- /dev/null +++ b/outscale/data_source_outscale_user.go @@ -0,0 +1,111 @@ +package outscale + +import ( + "context" + "fmt" + "log" + "time" + + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceUser() *schema.Resource { + return &schema.Resource{ + Read: DataSourceUserRead, + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DataSourceUserRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + filters, filtersOk := d.GetOk("filter") + req := oscgo.NewReadUsersRequest() + if filtersOk { + filterReq := buildUsersFilters(filters.(*schema.Set)) + req.SetFilters(*filterReq) + } + var resp oscgo.ReadUsersResponse + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserApi.ReadUsers(context.Background()).ReadUsersRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + users := resp.GetUsers() + d.SetId(resource.UniqueId()) + if len(users) == 0 { + return fmt.Errorf("Unable to find users") + } + if len(users) > 1 { + return fmt.Errorf("Find To many users") + } + + if err := d.Set("user_name", users[0].GetUserName()); err != nil { + return err + } + if err := d.Set("user_id", users[0].GetUserId()); err != nil { + return err + } + if err := d.Set("path", users[0].GetPath()); err != nil { + return err + } + if err := d.Set("creation_date", users[0].GetCreationDate()); err != nil { + return err + } + if err := d.Set("last_modification_date", users[0].GetLastModificationDate()); err != nil { + return err + } + return nil +} + +func buildUsersFilters(set *schema.Set) *oscgo.FiltersUsers { + var filters oscgo.FiltersUsers + for _, v := range set.List() { + m := v.(map[string]interface{}) + var filterValues []string + for _, e := range m["values"].([]interface{}) { + filterValues = append(filterValues, e.(string)) + } + + switch name := m["name"].(string); name { + case "user_ids": + filters.SetUserIds(filterValues) + default: + log.Printf("[Debug] Unknown Filter Name: %s.", name) + } + } + return &filters +} diff --git a/outscale/data_source_outscale_user_group.go b/outscale/data_source_outscale_user_group.go new file mode 100644 index 000000000..ee46c347d --- /dev/null +++ b/outscale/data_source_outscale_user_group.go @@ -0,0 +1,117 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourceUserGroup() *schema.Resource { + return &schema.Resource{ + Read: DataSourceUserGroupRead, + Schema: map[string]*schema.Schema{ + "user_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + }, + "user_group_id": { + Type: schema.TypeString, + Required: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + "user": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourceUserGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + + filters, filtersOk := d.GetOk("filter") + if !filtersOk { + return fmt.Errorf("One of filters, user_group_id or path_refix must be assigned") + } + + filterReq := buildUserGroupsFilters(filters.(*schema.Set)) + req := oscgo.ReadUserGroupsRequest{} + req.SetFilters(*filterReq) + var resp oscgo.ReadUserGroupsResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.ReadUserGroups(context.Background()).ReadUserGroupsRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetUserGroupsOk(); !ok { + return fmt.Errorf("Unable to find user groups") + } + d.SetId(resource.UniqueId()) + userGps := resp.GetUserGroups() + userGroups := make([]map[string]interface{}, len(userGps)) + + for i, v := range userGps { + userGroup := make(map[string]interface{}) + userGroup["user_group_name"] = v.GetName() + userGroup["user_group_id"] = v.GetUserGroupId() + userGroup["path"] = v.GetPath() + userGroup["orn"] = v.GetOrn() + userGroup["creation_date"] = v.GetCreationDate() + userGroup["last_modification_date"] = v.GetLastModificationDate() + userGroups[i] = userGroup + } + return d.Set("user_groups", userGroups) +} diff --git a/outscale/data_source_outscale_user_groups.go b/outscale/data_source_outscale_user_groups.go index 97526b650..1095e0647 100644 --- a/outscale/data_source_outscale_user_groups.go +++ b/outscale/data_source_outscale_user_groups.go @@ -57,7 +57,6 @@ func DataSourceUserGroupsRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI filters, filtersOk := d.GetOk("filter") - if !filtersOk { return fmt.Errorf("One of filters, user_group_id or path_refix must be assigned") } diff --git a/outscale/data_source_outscale_user_groups_test.go b/outscale/data_source_outscale_user_groups_test.go new file mode 100644 index 000000000..a13b46fef --- /dev/null +++ b/outscale/data_source_outscale_user_groups_test.go @@ -0,0 +1,37 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_user_groups_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_user_groups.basicUGTest" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataUserGroupsBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_groups.#"), + ), + }, + }, + }) +} + +const testAccDataUserGroupsBasicConfig = ` + resource "outscale_user_group" "uGroupData" { + user_group_name = "TestACC_uGdata" + path = "/" + } + data "outscale_user_groups" "basicUGTest" { + filter { + name = "user_group_ids" + values = [outscale_user_group.uGroupData.user_group_id] + } + }` diff --git a/outscale/data_source_outscale_user_test.go b/outscale/data_source_outscale_user_test.go new file mode 100644 index 000000000..5196da235 --- /dev/null +++ b/outscale/data_source_outscale_user_test.go @@ -0,0 +1,38 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_user_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_user.basicTestUser" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataUserBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_id"), + ), + }, + }, + }) +} + +const testAccDataUserBasicConfig = ` + resource "outscale_user" "basic_dataUser" { + user_name = "ACC_user_data1" + path = "/" + } + data "outscale_user" "basicTestUser" { + filter { + name = "user_id" + values = [outscale_user.basic_dataUser.user_id] + } + } +` diff --git a/outscale/data_source_outscale_users.go b/outscale/data_source_outscale_users.go index cece2454a..81edc1298 100644 --- a/outscale/data_source_outscale_users.go +++ b/outscale/data_source_outscale_users.go @@ -16,6 +16,7 @@ func DataSourceUsers() *schema.Resource { return &schema.Resource{ Read: DataSourceUsersRead, Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), "users": { Type: schema.TypeList, Computed: true, @@ -33,6 +34,14 @@ func DataSourceUsers() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -42,7 +51,12 @@ func DataSourceUsers() *schema.Resource { func DataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI + filters, filtersOk := d.GetOk("filter") req := oscgo.NewReadUsersRequest() + if filtersOk { + filterReq := buildUsersFilters(filters.(*schema.Set)) + req.SetFilters(*filterReq) + } var resp oscgo.ReadUsersResponse err := resource.Retry(2*time.Minute, func() *resource.RetryError { @@ -70,6 +84,8 @@ func DataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { user["user_id"] = v.GetUserId() user["user_name"] = v.GetUserName() user["path"] = v.GetPath() + user["creation_date"] = v.GetCreationDate() + user["last_modification_date"] = v.GetLastModificationDate() usersToSet[i] = user } return d.Set("users", usersToSet) diff --git a/outscale/data_source_outscale_users_test.go b/outscale/data_source_outscale_users_test.go new file mode 100644 index 000000000..d9950b824 --- /dev/null +++ b/outscale/data_source_outscale_users_test.go @@ -0,0 +1,34 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_users_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_users.basicTestUsers" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccOutscaleDataUserBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "users.#"), + ), + }, + }, + }) +} + +const testAccOutscaleDataUserBasicConfig = ` + resource "outscale_user" "basic_data_users" { + user_name = "ACC_test_data1" + path = "/" + } + data "outscale_users" "basicTestUsers" { + depends_on = [outscale_user.basic_data_users] + }` diff --git a/outscale/provider.go b/outscale/provider.go index 4cd7f763b..0e7dada11 100644 --- a/outscale/provider.go +++ b/outscale/provider.go @@ -116,7 +116,9 @@ func Provider() *schema.Provider { "outscale_api_access_policy": ResourceOutscaleApiAccessPolicy(), "outscale_main_route_table_link": resourceLinkMainRouteTable(), "outscale_user": ResourceOutscaleUser(), - "outscale_user_group": ResourceOutscaleUserGroup(), + "outscale_user_group": ResourceUserGroup(), + "outscale_policy": ResourceOutscalePolicy(), + "outscale_policy_version": ResourcePolicyVersion(), }, DataSourcesMap: map[string]*schema.Resource{ "outscale_vm": DataSourceOutscaleVM(), @@ -195,7 +197,11 @@ func Provider() *schema.Provider { "outscale_account": DataSourceAccount(), "outscale_accounts": DataSourceAccounts(), "outscale_users": DataSourceUsers(), + "outscale_user": DataSourceUser(), "outscale_user_groups": DataSourceUserGroups(), + "outscale_user_group": DataSourceUserGroup(), + "outscale_policy": DataSourcePolicy(), + "outscale_policies": DataSourcePolicies(), }, ConfigureFunc: providerConfigureClient, diff --git a/outscale/resource_outscale_policy.go b/outscale/resource_outscale_policy.go new file mode 100644 index 000000000..09af0c6c6 --- /dev/null +++ b/outscale/resource_outscale_policy.go @@ -0,0 +1,184 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func ResourceOutscalePolicy() *schema.Resource { + return &schema.Resource{ + Create: ResourceOutscalePolicyCreate, + Read: ResourceOutscalePolicyRead, + Delete: ResourceOutscalePolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "policy_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "document": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "resources_count": { + Type: schema.TypeInt, + Computed: true, + }, + "policy_default_version_id": { + Type: schema.TypeString, + Computed: true, + }, + "is_linkable": { + Type: schema.TypeBool, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func ResourceOutscalePolicyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + polDocument := d.Get("document").(string) + req := oscgo.NewCreatePolicyRequest(polDocument, d.Get("policy_name").(string)) + if polPath := d.Get("path").(string); polPath != "" { + req.SetPath(polPath) + } + if polDescription := d.Get("description").(string); polDescription != "" { + req.SetDescription(polDescription) + } + + var resp oscgo.CreatePolicyResponse + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.CreatePolicy(context.Background()).CreatePolicyRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return err + } + + d.SetId(resource.UniqueId()) + ply := resp.GetPolicy() + if err := d.Set("orn", ply.GetOrn()); err != nil { + return err + } + + // Remove d.Set when read user_group return user_group_id + return ResourceOutscalePolicyRead(d, meta) +} + +func ResourceOutscalePolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadPolicyRequest(d.Get("orn").(string)) + + var resp oscgo.ReadPolicyResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicy(context.Background()).ReadPolicyRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetPolicyOk(); !ok { + d.SetId("") + return nil + } + policy := resp.GetPolicy() + if err := d.Set("policy_name", policy.GetPolicyName()); err != nil { + return err + } + if err := d.Set("policy_id", policy.GetPolicyId()); err != nil { + return err + } + if err := d.Set("path", policy.GetPath()); err != nil { + return err + } + if err := d.Set("orn", policy.GetOrn()); err != nil { + return err + } + if err := d.Set("resources_count", policy.GetResourcesCount()); err != nil { + return err + } + if err := d.Set("is_linkable", policy.GetIsLinkable()); err != nil { + return err + } + if err := d.Set("policy_default_version_id", policy.GetPolicyDefaultVersionId()); err != nil { + return err + } + if err := d.Set("description", policy.GetDescription()); err != nil { + return err + } + + if err := d.Set("creation_date", (policy.GetCreationDate())); err != nil { + return err + } + + if err := d.Set("last_modification_date", (policy.GetLastModificationDate())); err != nil { + return err + } + return nil +} + +func ResourceOutscalePolicyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewDeletePolicyRequest(d.Get("orn").(string)) + var err error + err = resource.Retry(5*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.DeletePolicy(context.Background()).DeletePolicyRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return fmt.Errorf("Error deleting Outscale Policy %s: %s", d.Id(), err) + } + + return nil +} diff --git a/outscale/resource_outscale_policy_test.go b/outscale/resource_outscale_policy_test.go new file mode 100644 index 000000000..da22e88ae --- /dev/null +++ b/outscale/resource_outscale_policy_test.go @@ -0,0 +1,33 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_policy_basic(t *testing.T) { + t.Parallel() + resourceName := "outscale_policy.basic_policy" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPolicyBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "policy_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + ), + }, + }, + }) +} + +const testAccPolicyBasicConfig = ` + resource "outscale_policy" "basic_policy" { + policy_name = "TestACC_resoucePolicy" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/" + }` diff --git a/outscale/resource_outscale_policy_version.go b/outscale/resource_outscale_policy_version.go new file mode 100644 index 000000000..2fc45bffe --- /dev/null +++ b/outscale/resource_outscale_policy_version.go @@ -0,0 +1,160 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func ResourcePolicyVersion() *schema.Resource { + return &schema.Resource{ + Create: ResourcePolicyVersionCreate, + Read: ResourcePolicyVersionRead, + Delete: ResourcePolicyVersionDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "document": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "set_as_default": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "version_id": { + Type: schema.TypeString, + Computed: true, + }, + "default_version": { + Type: schema.TypeBool, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "body": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func ResourcePolicyVersionCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + pvDocument := d.Get("document").(string) + req := oscgo.NewCreatePolicyVersionRequest(pvDocument, d.Get("policy_orn").(string)) + + if asDefault, ok := d.GetOk("set_as_default"); ok { + req.SetSetAsDefault(asDefault.(bool)) + } + + var resp oscgo.CreatePolicyVersionResponse + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.CreatePolicyVersion(context.Background()).CreatePolicyVersionRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return err + } + + d.SetId(resource.UniqueId()) + pVersion := resp.GetPolicyVersion() + if err := d.Set("version_id", pVersion.GetVersionId()); err != nil { + return err + } + + // Remove d.Set when read user_group return user_group_id + return ResourcePolicyVersionRead(d, meta) +} + +func ResourcePolicyVersionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadPolicyVersionRequest(d.Get("policy_orn").(string), d.Get("version_id").(string)) + + var resp oscgo.ReadPolicyVersionResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicyVersion(context.Background()).ReadPolicyVersionRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetPolicyVersionOk(); !ok { + d.SetId("") + return nil + } + pVersion := resp.GetPolicyVersion() + if err := d.Set("default_version", pVersion.GetDefaultVersion()); err != nil { + return err + } + if err := d.Set("creation_date", (pVersion.GetCreationDate())); err != nil { + return err + } + if err := d.Set("body", pVersion.GetBody()); err != nil { + return err + } + /* + usrs := resp.GetUsers() + users := make([]map[string]interface{}, len(usrs)) + if len(usrs) > 0 { + usrs := resp.GetUsers() + for i, v := range usrs { + user := make(map[string]interface{}) + user["user_id"] = v.GetUserId() + user["user_name"] = v.GetUserName() + user["path"] = v.GetPath() + user["creation_date"] = v.GetCreationDate() + user["last_modification_date"] = v.GetLastModificationDate() + users[i] = user + } + } + if err := d.Set("users", users); err != nil { + return err + }*/ + return nil +} + +func ResourcePolicyVersionDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewDeletePolicyVersionRequest(d.Get("policy_orn").(string), d.Get("version_id").(string)) + var err error + err = resource.Retry(5*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.DeletePolicyVersion(context.Background()).DeletePolicyVersionRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return fmt.Errorf("Error deleting Outscale Policy version %s: %s", d.Id(), err) + } + + return nil +} diff --git a/outscale/resource_outscale_policy_version_test.go b/outscale/resource_outscale_policy_version_test.go new file mode 100644 index 000000000..83b72efc3 --- /dev/null +++ b/outscale/resource_outscale_policy_version_test.go @@ -0,0 +1,39 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_policy_Version_basic(t *testing.T) { + t.Parallel() + resourceName := "outscale_policy_version.policy_version" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPolicyVersionConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrSet(resourceName, "body"), + resource.TestCheckResourceAttr(resourceName, "version_id", "v2"), + ), + }, + }, + }) +} + +const testAccPolicyVersionConfig = ` + resource "outscale_policy" "vers_policy" { + policy_name = "TestACC_VersionPolicy" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/" + } + resource "outscale_policy_version" "policy_version" { + policy_orn = outscale_policy.vers_policy.orn + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + } +` diff --git a/outscale/resource_outscale_snapshot.go b/outscale/resource_outscale_snapshot.go index 69afed262..9b3db19ee 100644 --- a/outscale/resource_outscale_snapshot.go +++ b/outscale/resource_outscale_snapshot.go @@ -157,6 +157,10 @@ func ResourceOutscaleSnapshotCreate(d *schema.ResourceData, meta interface{}) er var err error rp, httpResp, err := conn.SnapshotApi.CreateSnapshot(context.Background()).CreateSnapshotRequest(request).Execute() if err != nil { + fmt.Printf("\nhttpResp1== %#v===\n", httpResp) + fmt.Printf("\nErr== %#v===\n", err.Error()) + fmt.Printf("\nErr== %#v===\n", rp) + fmt.Printf("\nhttpResp== %#v===\n", httpResp.Request) return utils.CheckThrottling(httpResp, err) } resp = rp diff --git a/outscale/resource_outscale_user.go b/outscale/resource_outscale_user.go index 479165510..33c309d8d 100644 --- a/outscale/resource_outscale_user.go +++ b/outscale/resource_outscale_user.go @@ -26,13 +26,49 @@ func ResourceOutscaleUser() *schema.Resource { Type: schema.TypeString, Required: true, }, + "path": { + Type: schema.TypeString, + Optional: true, + }, "user_id": { Type: schema.TypeString, Computed: true, }, - "path": { + "creation_date": { Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + "policy": { + Type: schema.TypeSet, Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + }, + "policy_name": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, }, }, } @@ -58,9 +94,25 @@ func ResourceOutscaleUserCreate(d *schema.ResourceData, meta interface{}) error if err != nil { return err } - d.SetId(resource.UniqueId()) - if err := d.Set("user_id", *resp.GetUser().UserId); err != nil { - return err + d.SetId(*resp.GetUser().UserId) + if policiesToAdd, ok := d.GetOk("policy"); ok { + reqAddPolicy := oscgo.LinkPolicyRequest{} + + for _, v := range policiesToAdd.(*schema.Set).List() { + policy := v.(map[string]interface{}) + reqAddPolicy.SetUserName(d.Get("user_name").(string)) + reqAddPolicy.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.LinkPolicy(context.Background()).LinkPolicyRequest(reqAddPolicy).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } } return ResourceOutscaleUserRead(d, meta) @@ -68,11 +120,13 @@ func ResourceOutscaleUserCreate(d *schema.ResourceData, meta interface{}) error func ResourceOutscaleUserRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI - req := oscgo.NewReadUsersRequest() + req := oscgo.ReadUsersRequest{ + Filters: &oscgo.FiltersUsers{UserIds: &[]string{d.Id()}}, + } var resp oscgo.ReadUsersResponse err := resource.Retry(2*time.Minute, func() *resource.RetryError { - rp, httpResp, err := conn.UserApi.ReadUsers(context.Background()).ReadUsersRequest(*req).Execute() + rp, httpResp, err := conn.UserApi.ReadUsers(context.Background()).ReadUsersRequest(req).Execute() if err != nil { return utils.CheckThrottling(httpResp, err) } @@ -82,63 +136,165 @@ func ResourceOutscaleUserRead(d *schema.ResourceData, meta interface{}) error { if err != nil { return err } - users := resp.GetUsers() if len(users) == 0 { d.SetId("") return nil } - for _, user := range users { - if user.GetUserId() == d.Get("user_id") { - - if err := d.Set("user_name", user.GetUserName()); err != nil { - return err - } - if err := d.Set("user_id", user.GetUserId()); err != nil { - return err - } - if err := d.Set("path", user.GetPath()); err != nil { - return err - } - break + linkReq := oscgo.NewReadLinkedPoliciesRequest(d.Get("user_name").(string)) + var linkResp oscgo.ReadLinkedPoliciesResponse + err = resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadLinkedPolicies(context.Background()).ReadLinkedPoliciesRequest(*linkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) } + linkResp = rp + return nil + }) + if err != nil { + return err + } + + if err := d.Set("user_name", users[0].GetUserName()); err != nil { + return err + } + if err := d.Set("user_id", users[0].GetUserId()); err != nil { + return err + } + if err := d.Set("path", users[0].GetPath()); err != nil { + return err + } + if err := d.Set("creation_date", users[0].GetCreationDate()); err != nil { + return err + } + if err := d.Set("last_modification_date", users[0].GetLastModificationDate()); err != nil { + return err } + uPolicies := linkResp.GetPolicies() + policies := make([]map[string]interface{}, len(uPolicies)) + if len(uPolicies) > 0 { + for i, v := range uPolicies { + policy := make(map[string]interface{}) + policy["policy_id"] = v.GetPolicyId() + policy["policy_name"] = v.GetPolicyName() + policy["policy_orn"] = v.GetOrn() + policy["creation_date"] = v.GetCreationDate() + policy["last_modification_date"] = v.GetLastModificationDate() + + policies[i] = policy + } + } + if err := d.Set("policy", policies); err != nil { + return err + } return nil } func ResourceOutscaleUserUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI req := oscgo.UpdateUserRequest{} + isUpdateUser := false - oldN, newN := d.GetChange("user_name") - if oldName := oldN.(string); oldName != "" { - req.SetUserName(oldName) - } - if newName := newN.(string); newName != "" && oldN.(string) != newN.(string) { - req.SetNewUserName(newName) + if d.HasChange("user_name") { + oldN, newN := d.GetChange("user_name") + if oldName := oldN.(string); oldName != "" { + req.SetUserName(oldName) + isUpdateUser = true + } + if newName := newN.(string); newName != "" && oldN.(string) != newN.(string) { + req.SetNewUserName(newName) + isUpdateUser = true + } } - if d.HasChange("path") { path := d.Get("path").(string) - req.NewPath = &path + req.SetNewPath(path) + isUpdateUser = true } + if d.HasChange("policy") { + oldPolicies, newPolicies := d.GetChange("policy") + inter := oldPolicies.(*schema.Set).Intersection(newPolicies.(*schema.Set)) + toCreate := newPolicies.(*schema.Set).Difference(inter) + toRemove := oldPolicies.(*schema.Set).Difference(inter) - err := resource.Retry(2*time.Minute, func() *resource.RetryError { - _, httpResp, err := conn.UserApi.UpdateUser(context.Background()).UpdateUserRequest(req).Execute() + if len(toRemove.List()) > 0 { + unlinkReq := oscgo.UnlinkPolicyRequest{} + oldN, _ := d.GetChange("user_name") + unlinkReq.SetUserName(oldN.(string)) + + for _, v := range toRemove.List() { + policy := v.(map[string]interface{}) + unlinkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.UnlinkPolicy(context.Background()).UnlinkPolicyRequest(unlinkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + if len(toCreate.List()) > 0 { + linkReq := oscgo.LinkPolicyRequest{} + oldN, _ := d.GetChange("user_name") + linkReq.SetUserName(oldN.(string)) + + for _, v := range toCreate.List() { + policy := v.(map[string]interface{}) + linkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.LinkPolicy(context.Background()).LinkPolicyRequest(linkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + } + if isUpdateUser { + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.UserApi.UpdateUser(context.Background()).UpdateUserRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) if err != nil { - return utils.CheckThrottling(httpResp, err) + return err } - return nil - }) - if err != nil { - return err } return ResourceOutscaleUserRead(d, meta) } func ResourceOutscaleUserDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI + if _, ok := d.GetOk("policy"); ok { + policies := d.Get("policy") + for _, v := range policies.(*schema.Set).List() { + unlinkReq := oscgo.UnlinkPolicyRequest{} + unlinkReq.SetUserName(d.Get("user_name").(string)) + policy := v.(map[string]interface{}) + unlinkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.UnlinkPolicy(context.Background()).UnlinkPolicyRequest(unlinkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } req := oscgo.DeleteUserRequest{ UserName: d.Get("user_name").(string), diff --git a/outscale/resource_outscale_user_group.go b/outscale/resource_outscale_user_group.go index 7ebb5791d..3b0bd8a80 100644 --- a/outscale/resource_outscale_user_group.go +++ b/outscale/resource_outscale_user_group.go @@ -11,12 +11,12 @@ import ( "github.com/outscale/terraform-provider-outscale/utils" ) -func ResourceOutscaleUserGroup() *schema.Resource { +func ResourceUserGroup() *schema.Resource { return &schema.Resource{ - Create: ResourceOutscaleUserGroupCreate, - Read: ResourceOutscaleUserGroupRead, - Update: ResourceOutscaleUserGroupUpdate, - Delete: ResourceOutscaleUserGroupDelete, + Create: ResourceUserGroupCreate, + Read: ResourceUserGroupRead, + Update: ResourceUserGroupUpdate, + Delete: ResourceUserGroupDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -46,7 +46,35 @@ func ResourceOutscaleUserGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "users": { + "policy": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + }, + "policy_name": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "user": { Type: schema.TypeSet, Optional: true, Elem: &schema.Resource{ @@ -79,7 +107,7 @@ func ResourceOutscaleUserGroup() *schema.Resource { } } -func ResourceOutscaleUserGroupCreate(d *schema.ResourceData, meta interface{}) error { +func ResourceUserGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI req := oscgo.NewCreateUserGroupRequest(d.Get("user_group_name").(string)) @@ -101,11 +129,10 @@ func ResourceOutscaleUserGroupCreate(d *schema.ResourceData, meta interface{}) e } d.SetId(resource.UniqueId()) - // Remove d.Set when read user_group return user_group_id if err := d.Set("user_group_id", resp.GetUserGroup().UserGroupId); err != nil { return err } - if usersToAdd, ok := d.GetOk("users"); ok { + if usersToAdd, ok := d.GetOk("user"); ok { reqUserAdd := oscgo.AddUserToUserGroupRequest{} reqUserAdd.SetUserGroupName(d.Get("user_group_name").(string)) if path := d.Get("path").(string); path != "" { @@ -137,16 +164,36 @@ func ResourceOutscaleUserGroupCreate(d *schema.ResourceData, meta interface{}) e } } - return ResourceOutscaleUserGroupRead(d, meta) + if policiesToAdd, ok := d.GetOk("policy"); ok { + reqAddPolicy := oscgo.LinkManagedPolicyToUserGroupRequest{} + + for _, v := range policiesToAdd.(*schema.Set).List() { + policy := v.(map[string]interface{}) + + reqAddPolicy.SetUserGroupName(d.Get("user_group_name").(string)) + reqAddPolicy.SetPolicyOrn(policy["policy_orn"].(string)) + + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.LinkManagedPolicyToUserGroup(context.Background()).LinkManagedPolicyToUserGroupRequest(reqAddPolicy).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + return ResourceUserGroupRead(d, meta) } -func ResourceOutscaleUserGroupRead(d *schema.ResourceData, meta interface{}) error { +func ResourceUserGroupRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI req := oscgo.NewReadUserGroupRequest(d.Get("user_group_name").(string)) if path := d.Get("path").(string); path != "" { - req.Path = &path + req.SetPath(path) } - var resp oscgo.ReadUserGroupResponse err := resource.Retry(2*time.Minute, func() *resource.RetryError { rp, httpResp, err := conn.UserGroupApi.ReadUserGroup(context.Background()).ReadUserGroupRequest(*req).Execute() @@ -156,7 +203,6 @@ func ResourceOutscaleUserGroupRead(d *schema.ResourceData, meta interface{}) err resp = rp return nil }) - if err != nil { return err } @@ -165,15 +211,24 @@ func ResourceOutscaleUserGroupRead(d *schema.ResourceData, meta interface{}) err d.SetId("") return nil } - userGroup := resp.GetUserGroup() - if err := d.Set("user_group_name", userGroup.GetName()); err != nil { + + linkReq := oscgo.NewReadManagedPoliciesLinkedToUserGroupRequest(d.Get("user_group_name").(string)) + var linkResp oscgo.ReadManagedPoliciesLinkedToUserGroupResponse + err = resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadManagedPoliciesLinkedToUserGroup(context.Background()).ReadManagedPoliciesLinkedToUserGroupRequest(*linkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + linkResp = rp + return nil + }) + if err != nil { return err } - /* Remove comment when read user_group return user_group_id - if err := d.Set("user_group_id", userGroup.GetUserGroupId()); err != nil { + userGroup := resp.GetUserGroup() + if err := d.Set("user_group_name", userGroup.GetName()); err != nil { return err } - */ if err := d.Set("path", userGroup.GetPath()); err != nil { return err } @@ -200,13 +255,31 @@ func ResourceOutscaleUserGroupRead(d *schema.ResourceData, meta interface{}) err users[i] = user } } - if err := d.Set("users", users); err != nil { + if err := d.Set("user", users); err != nil { + return err + } + + gPolicies := linkResp.GetPolicies() + policies := make([]map[string]interface{}, len(gPolicies)) + if len(gPolicies) > 0 { + for i, v := range gPolicies { + policy := make(map[string]interface{}) + policy["policy_id"] = v.GetPolicyId() + policy["policy_name"] = v.GetPolicyName() + policy["policy_orn"] = v.GetOrn() + policy["creation_date"] = v.GetCreationDate() + policy["last_modification_date"] = v.GetLastModificationDate() + + policies[i] = policy + } + } + if err := d.Set("policy", policies); err != nil { return err } return nil } -func ResourceOutscaleUserGroupUpdate(d *schema.ResourceData, meta interface{}) error { +func ResourceUserGroupUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI req := oscgo.UpdateUserGroupRequest{} isUpdateGroup := false @@ -228,8 +301,8 @@ func ResourceOutscaleUserGroupUpdate(d *schema.ResourceData, meta interface{}) e req.SetNewPath(newPath) isUpdateGroup = true } - if d.HasChange("users") { - oldUsers, newUsers := d.GetChange("users") + if d.HasChange("user") { + oldUsers, newUsers := d.GetChange("user") inter := oldUsers.(*schema.Set).Intersection(newUsers.(*schema.Set)) toCreate := newUsers.(*schema.Set).Difference(inter) toRemove := oldUsers.(*schema.Set).Difference(inter) @@ -247,7 +320,7 @@ func ResourceOutscaleUserGroupUpdate(d *schema.ResourceData, meta interface{}) e rmUserReq.SetUserName(userName) } if path := user["path"].(string); path != "" { - rmUserReq.SetUserGroupPath(path) + rmUserReq.SetUserPath(path) } err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, httpResp, err := conn.UserGroupApi.RemoveUserFromUserGroup(context.Background()).RemoveUserFromUserGroupRequest(rmUserReq).Execute() @@ -290,6 +363,55 @@ func ResourceOutscaleUserGroupUpdate(d *schema.ResourceData, meta interface{}) e } } } + + if d.HasChange("policy") { + oldPolicies, newPolicies := d.GetChange("policy") + inter := oldPolicies.(*schema.Set).Intersection(newPolicies.(*schema.Set)) + toCreate := newPolicies.(*schema.Set).Difference(inter) + toRemove := oldPolicies.(*schema.Set).Difference(inter) + + if len(toRemove.List()) > 0 { + unlinkReq := oscgo.UnlinkManagedPolicyFromUserGroupRequest{} + oldN, _ := d.GetChange("user_group_name") + unlinkReq.SetUserGroupName(oldN.(string)) + + for _, v := range toRemove.List() { + policy := v.(map[string]interface{}) + unlinkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.UnlinkManagedPolicyFromUserGroup(context.Background()).UnlinkManagedPolicyFromUserGroupRequest(unlinkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + if len(toCreate.List()) > 0 { + linkReq := oscgo.LinkManagedPolicyToUserGroupRequest{} + oldN, _ := d.GetChange("user_group_name") + linkReq.SetUserGroupName(oldN.(string)) + + for _, v := range toCreate.List() { + policy := v.(map[string]interface{}) + linkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.LinkManagedPolicyToUserGroup(context.Background()).LinkManagedPolicyToUserGroupRequest(linkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + + } + } + } if isUpdateGroup { err := resource.Retry(2*time.Minute, func() *resource.RetryError { _, httpResp, err := conn.UserGroupApi.UpdateUserGroup(context.Background()).UpdateUserGroupRequest(req).Execute() @@ -302,10 +424,10 @@ func ResourceOutscaleUserGroupUpdate(d *schema.ResourceData, meta interface{}) e return err } } - return ResourceOutscaleUserGroupRead(d, meta) + return ResourceUserGroupRead(d, meta) } -func ResourceOutscaleUserGroupDelete(d *schema.ResourceData, meta interface{}) error { +func ResourceUserGroupDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI forceDeletion := true req := oscgo.DeleteUserGroupRequest{ diff --git a/outscale/resource_outscale_user_group_test.go b/outscale/resource_outscale_user_group_test.go new file mode 100644 index 000000000..4cb271185 --- /dev/null +++ b/outscale/resource_outscale_user_group_test.go @@ -0,0 +1,132 @@ +package outscale + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_user_group_basic(t *testing.T) { + t.Parallel() + resourceName := "outscale_user_group.basic_group" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_group_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + ), + }, + }, + }) +} +func TestAccOthers_userGroup_with_user(t *testing.T) { + t.Parallel() + resourceName := "outscale_user_group.userGroupAcc" + groupName := "groupWithUsers" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupWithUsers(groupName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_group_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_group_name", groupName), + ), + }, + }, + }) +} + +func TestAccOthers_userGroup_update(t *testing.T) { + t.Parallel() + resourceName := "outscale_user_group.userGroupTAcc1" + groupName := "Gp1UpUser" + userName := "userGp1" + newGpName := "Gp2UpUsers" + newUsName := "userGp2" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupUpadate(groupName, userName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_group_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_group_name", groupName), + ), + }, + { + Config: testAccUserGroupUpadate(newGpName, newUsName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_group_name"), + resource.TestCheckResourceAttrSet(resourceName, "users.#"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_group_name", newGpName), + ), + }, + }, + }) +} + +const testAccUserGroupBasicConfig = ` + resource "outscale_user_group" "basic_group" { + user_group_name = "TestACC_group1" + path = "/" + }` + +func testAccUserGroupWithUsers(name string) string { + return fmt.Sprintf(` + resource "outscale_user" "userToAdd1" { + user_name = "userForGp1" + path = "/" + } + resource "outscale_user" "userToAdd2" { + user_name = "userForGp2" + path = "/TestPath/" + } + + resource "outscale_user_group" "userGroupAcc" { + user_group_name = "%s" + path = "/" + users { + user_name = outscale_user.userToAdd1.user_name + } + users { + user_name = outscale_user.userToAdd2.user_name + path = "/TestPath/" + } + } + `, name) +} + +func testAccUserGroupUpadate(name, userName string) string { + return fmt.Sprintf(` + resource "outscale_user" "userUpToAdd01" { + user_name = "userGp1" + path = "/" + } + resource "outscale_user" "userUpToAdd02" { + user_name = "userGp2" + path = "/TestPath/" + } + resource "outscale_user_group" "userGroupTAcc1" { + user_group_name = "%s" + path = "/" + users { + user_name = "%s" + } + depends_on = [outscale_user.userUpToAdd01] + } + `, name, userName) +} diff --git a/outscale/resource_outscale_user_test.go b/outscale/resource_outscale_user_test.go new file mode 100644 index 000000000..e853de90e --- /dev/null +++ b/outscale/resource_outscale_user_test.go @@ -0,0 +1,73 @@ +package outscale + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_User_basic(t *testing.T) { + t.Parallel() + resourceName := "outscale_user.basic_user" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccOutscaleUserBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + ), + }, + }, + }) +} + +func TestAccOthers_User_update(t *testing.T) { + t.Parallel() + resourceName := "outscale_user.update_user" + name := "TestACC_user1" + newName := "TestACC_user2" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccOutscaleUserUpdatedConfig(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_name", name), + ), + }, + { + Config: testAccOutscaleUserUpdatedConfig(newName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "path"), + resource.TestCheckResourceAttrSet(resourceName, "user_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_name", newName), + ), + }, + }, + }) +} + +const testAccOutscaleUserBasicConfig = ` + resource "outscale_user" "basic_user" { + user_name = "ACC_test1" + path = "/" + }` + +func testAccOutscaleUserUpdatedConfig(name string) string { + return fmt.Sprintf(` + resource "outscale_user" "update_user" { + user_name = "%s" + path = "/" + } + `, name) +} diff --git a/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.ref b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.ref new file mode 100644 index 000000000..c37f9edb5 --- /dev/null +++ b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.ref @@ -0,0 +1,29 @@ +{ + "version": "########", + "terraform_version": "########", + "serial": "########", + "lineage": "########", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "outscale_user", + "name": "userInteg", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "##id-0##", + "path": "/", + "user_id": "##id-1##", + "user_name": "test_integ" + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + } + ], + "check_results": "########" +} \ No newline at end of file diff --git a/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.tf b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.tf new file mode 100644 index 000000000..09b10093f --- /dev/null +++ b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.tf @@ -0,0 +1,4 @@ +resource "outscale_user" "userInteg" { + user_name = "test_integ" + path = "/" +} diff --git a/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.ref b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.ref new file mode 100644 index 000000000..71536b07b --- /dev/null +++ b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.ref @@ -0,0 +1,29 @@ +{ + "version": "########", + "terraform_version": "########", + "serial": "########", + "lineage": "########", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "outscale_user", + "name": "userInteg", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "##id-0##", + "path": "/Integ/", + "user_id": "##id-1##", + "user_name": "test_integ_update" + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + } + ], + "check_results": "########" +} \ No newline at end of file diff --git a/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.tf b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.tf new file mode 100644 index 000000000..e0f24a622 --- /dev/null +++ b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.tf @@ -0,0 +1,4 @@ +resource "outscale_user" "userInteg" { + user_name = "test_integ_update" + path = "/Integ/" +} diff --git a/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.ref b/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.ref new file mode 100644 index 000000000..6a8084d83 --- /dev/null +++ b/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.ref @@ -0,0 +1,59 @@ +{ + "version": "########", + "terraform_version": "########", + "serial": "########", + "lineage": "########", + "outputs": {}, + "resources": [ + { + "mode": "data", + "type": "outscale_user_groups", + "name": "testgrpData", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "filter": "########", + "id": "##id-0##", + "user_groups": [ + { + "creation_date": "########", + "last_modification_date": "########", + "orn": "########", + "path": "/TestdataUG/", + "user_group_id": "##id-1##", + "user_group_name": "testDataugInteg" + } + ] + }, + "sensitive_attributes": [] + } + ] + }, + { + "mode": "managed", + "type": "outscale_user_group", + "name": "dataUserGroupInteg", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "creation_date": "########", + "id": "##id-2##", + "last_modification_date": "########", + "orn": "########", + "path": "/TestdataUG/", + "user_group_id": "##id-1##", + "user_group_name": "testDataugInteg", + "users": [] + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + } + ], + "check_results": "########" +} \ No newline at end of file diff --git a/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.tf b/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.tf new file mode 100644 index 000000000..f41309561 --- /dev/null +++ b/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.tf @@ -0,0 +1,15 @@ +resource "outscale_user_group" "dataUserGroupInteg" { + user_group_name = "testDataugInteg" + path = "/TestdataUG/" +} + +data "outscale_user_groups" "testgrpData" { + filter { + name = "user_group_ids" + values = [outscale_user_group.dataUserGroupInteg.user_group_id] + } + filter { + name = "path_prefix" + values = ["/TestdataUG/"] + } +}