Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new resource WorkforcePool #6719

Merged
merged 8 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions mmv1/products/iamworkforcepool/api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Copyright 2022 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

--- !ruby/object:Api::Product
name: IAMWorkforcePool
display_name: Cloud IAM
versions:
- !ruby/object:Api::Product::Version
name: beta
base_url: https://iam.googleapis.com/v1/
scopes:
- https://www.googleapis.com/auth/iam
apis_required:
- !ruby/object:Api::Product::ApiReference
name: Identity and Access Management (IAM) API
url: https://console.cloud.google.com/apis/library/iam.googleapis.com/
async: !ruby/object:Api::OpAsync
operation: !ruby/object:Api::OpAsync::Operation
path: 'name'
base_url: '{{op_id}}'
wait_ms: 1000
result: !ruby/object:Api::OpAsync::Result
path: 'targetLink'
status: !ruby/object:Api::OpAsync::Status
path: 'done'
complete: True
allowed:
- true
- false
error: !ruby/object:Api::OpAsync::Error
path: 'error'
message: 'message'
objects:
- !ruby/object:Api::Resource
name: 'WorkforcePool'
base_url: locations/{{location}}/workforcePools
self_link: locations/{{location}}/workforcePools/{{workforce_pool_id}}
create_url: locations/{{location}}/workforcePools?workforcePoolId={{workforce_pool_id}}
update_verb: :PATCH
update_mask: true
min_version: beta
description: |
Represents a collection of external workforces. Provides namespaces for
slevenick marked this conversation as resolved.
Show resolved Hide resolved
federated users that can be referenced in IAM policies.
Note: Ask your Google Cloud account team to request access to workforce identity
federation for your billing/quota project. The account team notifies you when the project is
granted access.
references: !ruby/object:Api::Resource::ReferenceLinks
guides:
'Manage pools':
'https://cloud.google.com/iam/docs/manage-workforce-identity-pools-providers#manage_pools'
api: 'https://cloud.google.com/iam/docs/reference/rest/v1/locations.workforcePools'
properties:
- !ruby/object:Api::Type::String
name: 'location'
description: The location for the resource.
slevenick marked this conversation as resolved.
Show resolved Hide resolved
required: true
input: true
url_param_only: true
- !ruby/object:Api::Type::String
name: 'workforcePoolId'
description: |
The name of the pool. The ID must be a globally unique string of 6 to 63 lowercase letters,
digits, or hyphens. It must start with a letter, and cannot have a trailing hyphen.
The prefix `gcp-` is reserved for use by Google, and may not be specified.
required: true
input: true
url_param_only: true
- !ruby/object:Api::Type::String
name: 'name'
description: |
Output only. The resource name of the pool.
Format: `locations/{location}/workforcePools/{workforcePoolId}`
output: true
- !ruby/object:Api::Type::String
name: 'parent'
slevenick marked this conversation as resolved.
Show resolved Hide resolved
description: |
Immutable. The resource name of the parent. Format: `organizations/{org-id}`.
slevenick marked this conversation as resolved.
Show resolved Hide resolved
slevenick marked this conversation as resolved.
Show resolved Hide resolved
required: true
input: true
- !ruby/object:Api::Type::String
name: 'displayName'
description: A user-specified display name of the pool in Google Cloud Console. Cannot exceed 32 characters.
- !ruby/object:Api::Type::String
name: 'description'
description: A user-specified description of the pool. Cannot exceed 256 characters.
- !ruby/object:Api::Type::Enum
name: 'state'
description: |
Output only. The state of the pool.
* STATE_UNSPECIFIED: State unspecified.
* ACTIVE: The pool is active, and may be used in Google Cloud policies.
* DELETED: The pool is soft-deleted. Soft-deleted pools are permanently deleted
after approximately 30 days. You can restore a soft-deleted pool using
[UndeleteWorkforcePool][WorkforcePools.UndeleteWorkforcePool].
You cannot reuse the ID of a soft-deleted pool until it is permanently deleted.
While a pool is deleted, you cannot use it to exchange tokens, or use
existing tokens to access resources. If the pool is undeleted, existing
tokens grant access again.
output: true
values:
- :STATE_UNSPECIFIED
- :ACTIVE
- :DELETED
- !ruby/object:Api::Type::Boolean
name: 'disabled'
description: |
Whether the pool is disabled. You cannot use a disabled pool to exchange tokens,
or use existing tokens to access resources. If the pool is re-enabled, existing tokens grant access again.
- !ruby/object:Api::Type::String
name: 'sessionDuration'
description: |
Duration that the Google Cloud access tokens, console sign-in sessions,
and `gcloud` sign-in sessions from this pool are valid.
Must be greater than 15 minutes (900s) and less than 12 hours (43200s).
If `sessionDuration` is not configured, minted credentials have a default duration of one hour (3600s).
A duration in seconds with up to nine fractional digits, ending with '`s`'. Example: "`3.5s`".
default_value: '3600s'
50 changes: 50 additions & 0 deletions mmv1/products/iamworkforcepool/terraform.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright 2022 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

--- !ruby/object:Provider::Terraform::Config
legacy_name: iam
overrides: !ruby/object:Overrides::ResourceOverrides
WorkforcePool: !ruby/object:Overrides::Terraform::ResourceOverride
autogen_async: true
import_format: [ "locations/{{location}}/workforcePools/{{workforce_pool_id}}" ]
examples:
- !ruby/object:Provider::Terraform::Examples
name: "iam_workforce_pool_basic"
min_version: beta
primary_resource_id: "example"
vars:
workforce_pool_id: "example-pool"
test_env_vars:
org_id: :ORG_ID
- !ruby/object:Provider::Terraform::Examples
name: "iam_workforce_pool_full"
min_version: beta
primary_resource_id: "example"
vars:
workforce_pool_id: "example-pool"
test_env_vars:
org_id: :ORG_ID
custom_code: !ruby/object:Provider::Terraform::CustomCode
constants: templates/terraform/constants/iam_workforce_pool.go.erb
decoder: templates/terraform/decoders/treat_deleted_state_as_gone.go.erb
test_check_destroy: templates/terraform/custom_check_destroy/iam_workforce_pool.go.erb
properties:
workforcePoolId: !ruby/object:Overrides::Terraform::PropertyOverride
validation: !ruby/object:Provider::Terraform::Validation
function: 'validateWorkforcePoolId'
# This is for copying files over
files: !ruby/object:Provider::Config::Files
# These files have templating (ERB) code that will be run.
# This is usually to add licensing info, autogeneration notices, etc.
compile:
<%= lines(indent(compile('provider/terraform/product~compile.yaml'), 4)) -%>
20 changes: 20 additions & 0 deletions mmv1/templates/terraform/constants/iam_workforce_pool.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const workforcePoolIdRegexp = `^[a-z][a-z0-9-]{4,61}[a-z0-9]$`

func validateWorkforcePoolId(v interface{}, k string) (ws []string, errors []error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this much effort client-side to validate names?

Typically we avoid adding too much validation client-side and let the server validate fields like this, as validation tends to change over time. If the validation changes server-side, clients don't need to change to use it, but if it's client-side and needs to change it causes delays in users being able to use the new validation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I will delete some of the checks. Thank you for pointing this out.

value := v.(string)

if strings.HasPrefix(value, "gcp-") {
errors = append(errors, fmt.Errorf(
"%q (%q) can not start with \"gcp-\". " +
"The prefix `gcp-` is reserved for use by Google, and may not be specified.", k, value))
}

if !regexp.MustCompile(workforcePoolIdRegexp).MatchString(value) {
errors = append(errors, fmt.Errorf(
"Workforce Pool Id \"%q\" must contain only lowercase letters [a-z], digits [0-9], and hyphens " +
"[-]. The WorkforcePool ID must be between 6 and 63 characters, begin " +
"with a letter, and cannot have a trailing hyphen.", k))
}

return
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
config := googleProviderConfig(t)

url, err := replaceVarsForTest(config, rs, "{{IAMBetaBasePath}}locations/{{location}}/workforcePools/{{workforce_pool_id}}")
if err != nil {
return err
}

res, err := sendRequest(config, "GET", "", url, config.userAgent, nil)
if err != nil {
return nil
}

if v := res["state"]; v == "DELETED" {
return nil
}

return fmt.Errorf("IAMWorkforcePool still exists at %s", url)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "google_iam_workforce_pool" "<%= ctx[:primary_resource_id] %>" {
provider = google-beta

workforce_pool_id = "<%= ctx[:vars]["workforce_pool_id"] %>"
parent = "organizations/<%= ctx[:test_env_vars]["org_id"] %>"
location = "global"
}
11 changes: 11 additions & 0 deletions mmv1/templates/terraform/examples/iam_workforce_pool_full.tf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resource "google_iam_workforce_pool" "<%= ctx[:primary_resource_id] %>" {
provider = google-beta

workforce_pool_id = "<%= ctx[:vars]["workforce_pool_id"] %>"
parent = "organizations/<%= ctx[:test_env_vars]["org_id"] %>"
location = "global"
display_name = "Display name"
description = "A sample workforce pool."
disabled = false
session_duration = "7200s"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<% autogen_exception -%>
package google

<% unless version == 'ga' -%>

import (
"strings"
"testing"
)

func TestValidateIAMWorkforcePoolWorkforcePoolId(t *testing.T) {
x := []StringValidationTestCase{
// No errors
{TestName: "with numbers", Value: "foobar123"},
{TestName: "short", Value: "foobar"},
{TestName: "long", Value: strings.Repeat("f", 63)},
{TestName: "has a hyphen", Value: "foo-bar"},

// With errors
{TestName: "empty", Value: "", ExpectError: true},
{TestName: "starts with a gcp-", Value: "gcp-foobar", ExpectError: true},
{TestName: "with uppercase", Value: "fooBar", ExpectError: true},
{TestName: "has an slash", Value: "foo/bar", ExpectError: true},
{TestName: "has an backslash", Value: "foo\bar", ExpectError: true},
{TestName: "too short", Value: "foooo", ExpectError: true},
{TestName: "too long", Value: strings.Repeat("f", 64), ExpectError: true},
{TestName: "doesn't start with a lowercase letter", Value: "123foo", ExpectError: true},
{TestName: "ends with a hyphen", Value: "foobar-", ExpectError: true},
}

es := testStringValidationCases(x, validateWorkforcePoolId)
if len(es) > 0 {
t.Errorf("Failed to validate WorkforcePool names: %v", es)
}
}

<% end -%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<% autogen_exception -%>
package google

<% unless version == 'ga' -%>

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"testing"
)

func TestAccIAMWorkforcePoolWorkforcePool_full(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"org_id": getTestOrgFromEnv(t),
"random_suffix": randString(t, 10),
}

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckIAMWorkforcePoolWorkforcePoolDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccIAMWorkforcePoolWorkforcePool_full(context),
},
{
ResourceName: "google_iam_workforce_pool.my_pool",
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccIAMWorkforcePoolWorkforcePool_update(context),
},
{
ResourceName: "google_iam_workforce_pool.my_pool",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccIAMWorkforcePoolWorkforcePool_minimal(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"org_id": getTestOrgFromEnv(t),
"random_suffix": randString(t, 10),
}

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckIAMWorkforcePoolWorkforcePoolDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccIAMWorkforcePoolWorkforcePool_minimal(context),
},
{
ResourceName: "google_iam_workforce_pool.my_pool",
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccIAMWorkforcePoolWorkforcePool_update(context),
},
{
ResourceName: "google_iam_workforce_pool.my_pool",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccIAMWorkforcePoolWorkforcePool_full(context map[string]interface{}) string {
return Nprintf(`
resource "google_iam_workforce_pool" "my_pool" {
workforce_pool_id = "my-pool-%{random_suffix}"
parent = "organizations/%{org_id}"
location = "global"
display_name = "Display name"
description = "A sample workforce pool."
disabled = false
session_duration = "7200s"
}
`, context)
}

func testAccIAMWorkforcePoolWorkforcePool_minimal(context map[string]interface{}) string {
return Nprintf(`
resource "google_iam_workforce_pool" "my_pool" {
workforce_pool_id = "my-pool-%{random_suffix}"
parent = "organizations/%{org_id}"
location = "global"
}
`, context)
}

func testAccIAMWorkforcePoolWorkforcePool_update(context map[string]interface{}) string {
return Nprintf(`
resource "google_iam_workforce_pool" "my_pool" {
workforce_pool_id = "my-pool-%{random_suffix}"
parent = "organizations/%{org_id}"
location = "global"
display_name = "New display name"
description = "A sample workforce pool with updated description."
disabled = true
session_duration = "3600s"
}
`, context)
}
<% end -%>