Skip to content

TPT 2101: add users data source #895

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

Merged
merged 14 commits into from
Jun 27, 2023
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
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1
github.com/hashicorp/terraform-plugin-testing v1.3.0
github.com/linode/linodego v1.17.0
github.com/linode/linodego v1.17.2
github.com/linode/linodego/k8s v0.0.0-20200831124119-58d5d5bb7947
golang.org/x/crypto v0.10.0
)
Expand Down Expand Up @@ -67,7 +67,7 @@ require (
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/zclconf/go-cty v1.13.2 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/term v0.9.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/linode/linodego v0.20.1/go.mod h1:XOWXRHjqeU2uPS84tKLgfWIfTlv3TYzCS0io4GOQzEI=
github.com/linode/linodego v1.17.0 h1:aWS98f0jUoY2lhsEuBxRdVkqyGM0nazPd68AEDF0EvU=
github.com/linode/linodego v1.17.0/go.mod h1:/omzPxie0/YI6S0sTw1q47qDt5IYSlbO/infRR4UG+A=
github.com/linode/linodego v1.17.2 h1:b32dj4662PGG5P9qVa6nBezccWdqgukndlMIuPGq1CQ=
github.com/linode/linodego v1.17.2/go.mod h1:C2iyT3Vg2O2sPxkWka4XAQ5WSUtm5LmTZ3Adw43Ra7Q=
github.com/linode/linodego/k8s v0.0.0-20200831124119-58d5d5bb7947 h1:e+tpC7AIiEgfYGEDq9Rjtdybq+V10S6OXzWjeGV/CEk=
github.com/linode/linodego/k8s v0.0.0-20200831124119-58d5d5bb7947/go.mod h1:MWI0tFyaJqRpirMv0VO7CGYT4V3IhHvml2rs/DlRQmY=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
Expand Down Expand Up @@ -484,8 +484,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down
2 changes: 2 additions & 0 deletions linode/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"github.com/linode/terraform-provider-linode/linode/stackscripts"
"github.com/linode/terraform-provider-linode/linode/token"
"github.com/linode/terraform-provider-linode/linode/user"
"github.com/linode/terraform-provider-linode/linode/users"
"github.com/linode/terraform-provider-linode/linode/vlan"
"github.com/linode/terraform-provider-linode/linode/volume"
)
Expand Down Expand Up @@ -179,6 +180,7 @@ func (p *FrameworkProvider) DataSources(ctx context.Context) []func() datasource
databaseengines.NewDataSource,
region.NewDataSource,
vlan.NewDataSource,
users.NewDataSource,
nbnode.NewDataSource,
accountsettings.NewDataSource,
}
Expand Down
1 change: 1 addition & 0 deletions linode/user/datasource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestAccDataSourceUser_basic(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(resourceName, "username"),
resource.TestCheckResourceAttrSet(resourceName, "email"),
resource.TestCheckResourceAttrSet(resourceName, "tfa_enabled"),
),
},
{
Expand Down
6 changes: 4 additions & 2 deletions linode/user/framework_datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (d *DataSource) Read(
return
}

resp.Diagnostics.Append(data.parseUser(ctx, user)...)
resp.Diagnostics.Append(data.ParseUser(ctx, user)...)
if resp.Diagnostics.HasError() {
return
}
Expand All @@ -56,7 +56,9 @@ func (d *DataSource) Read(
)
return
}
resp.Diagnostics.Append(data.parseUserGrants(ctx, grants)...)
resp.Diagnostics.Append(data.ParseUserGrants(ctx, grants)...)
} else {
data.ParseNonUserGrants()
}
if resp.Diagnostics.HasError() {
return
Expand Down
100 changes: 59 additions & 41 deletions linode/user/framework_datasource_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,46 +38,64 @@ var linodeUserGrantsEntitySet = schema.SetAttribute{
ElementType: linodeUserGrantsEntityObjectType,
}

var frameworkDatasourceSchema = schema.Schema{
Attributes: map[string]schema.Attribute{
"username": schema.StringAttribute{
Description: "This User's username. This is used for logging in, and may also be displayed alongside " +
"actions the User performs (for example, in Events or public StackScripts).",
Required: true,
},
"ssh_keys": schema.ListAttribute{
Description: "A list of SSH Key labels added by this User. These are the keys that will be deployed " +
"if this User is included in the authorized_users field of a create Linode, rebuild Linode, or " +
"create Disk request.",
Computed: true,
ElementType: types.StringType,
},
"email": schema.StringAttribute{
Description: "The email address for this User, for account management communications, and may be used " +
"for other communications as configured.",
Computed: true,
},
"restricted": schema.BoolAttribute{
Description: "If true, this User must be granted access to perform actions or access entities on this Account.",
Computed: true,
},
"global_grants": schema.ListAttribute{
Description: "A structure containing the Account-level grants a User has.",
Computed: true,
ElementType: linodeUserGrantsGlobalObjectType,
},
"database_grant": linodeUserGrantsEntitySet,
"domain_grant": linodeUserGrantsEntitySet,
"firewall_grant": linodeUserGrantsEntitySet,
"image_grant": linodeUserGrantsEntitySet,
"linode_grant": linodeUserGrantsEntitySet,
"longview_grant": linodeUserGrantsEntitySet,
"nodebalancer_grant": linodeUserGrantsEntitySet,
"stackscript_grant": linodeUserGrantsEntitySet,
"volume_grant": linodeUserGrantsEntitySet,
"id": schema.StringAttribute{
Description: "Unique identifier for this DataSource.",
Computed: true,
},
var UserAttributes = map[string]schema.Attribute{
"username": schema.StringAttribute{
Description: "This User's username. This is used for logging in, and may also be displayed alongside " +
"actions the User performs (for example, in Events or public StackScripts).",
Required: true,
},
"ssh_keys": schema.ListAttribute{
Description: "A list of SSH Key labels added by this User. These are the keys that will be deployed " +
"if this User is included in the authorized_users field of a create Linode, rebuild Linode, or " +
"create Disk request.",
Computed: true,
ElementType: types.StringType,
},
"email": schema.StringAttribute{
Description: "The email address for this User, for account management communications, and may be used " +
"for other communications as configured.",
Computed: true,
},
"restricted": schema.BoolAttribute{
Description: "If true, this User must be granted access to perform actions or access entities on this Account.",
Computed: true,
},
"global_grants": schema.ListAttribute{
Description: "A structure containing the Account-level grants a User has.",
Computed: true,
ElementType: linodeUserGrantsGlobalObjectType,
},
"database_grant": linodeUserGrantsEntitySet,
"domain_grant": linodeUserGrantsEntitySet,
"firewall_grant": linodeUserGrantsEntitySet,
"image_grant": linodeUserGrantsEntitySet,
"linode_grant": linodeUserGrantsEntitySet,
"longview_grant": linodeUserGrantsEntitySet,
"nodebalancer_grant": linodeUserGrantsEntitySet,
"stackscript_grant": linodeUserGrantsEntitySet,
"volume_grant": linodeUserGrantsEntitySet,
"id": schema.StringAttribute{
Description: "Unique identifier for this DataSource.",
Computed: true,
},
"password_created": schema.StringAttribute{
Description: "The date and time when this User’s current password was created." +
"User passwords are first created during the Account sign-up process, " +
"and updated using the Reset Password webpage." +
"null if this User has not created a password yet.",
Computed: true,
},
"tfa_enabled": schema.BoolAttribute{
Description: "A boolean value indicating if the User has Two Factor Authentication (TFA) enabled.",
Computed: true,
},
"verified_phone_number": schema.StringAttribute{
Description: "The phone number verified for this User Profile with the Phone Number Verify command." +
"null if this User Profile has no verified phone number.",
Computed: true,
},
}

var frameworkDatasourceSchema = schema.Schema{
Attributes: UserAttributes,
}
59 changes: 42 additions & 17 deletions linode/user/framework_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package user
import (
"context"
"encoding/json"
"time"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
Expand All @@ -12,29 +13,40 @@ import (
)

type DataSourceModel struct {
Username types.String `tfsdk:"username"`
SSHKeys types.List `tfsdk:"ssh_keys"`
Email types.String `tfsdk:"email"`
Restricted types.Bool `tfsdk:"restricted"`
GlobalGrants types.List `tfsdk:"global_grants"`
DomainGrant types.Set `tfsdk:"domain_grant"`
FirewallGrant types.Set `tfsdk:"firewall_grant"`
ImageGrant types.Set `tfsdk:"image_grant"`
LinodeGrant types.Set `tfsdk:"linode_grant"`
LongviewGrant types.Set `tfsdk:"longview_grant"`
NodebalancerGrant types.Set `tfsdk:"nodebalancer_grant"`
StackscriptGrant types.Set `tfsdk:"stackscript_grant"`
VolumeGrant types.Set `tfsdk:"volume_grant"`
DatabaseGrant types.Set `tfsdk:"database_grant"`
ID types.String `tfsdk:"id"`
Username types.String `tfsdk:"username"`
SSHKeys types.List `tfsdk:"ssh_keys"`
Email types.String `tfsdk:"email"`
Restricted types.Bool `tfsdk:"restricted"`
GlobalGrants types.List `tfsdk:"global_grants"`
DomainGrant types.Set `tfsdk:"domain_grant"`
FirewallGrant types.Set `tfsdk:"firewall_grant"`
ImageGrant types.Set `tfsdk:"image_grant"`
LinodeGrant types.Set `tfsdk:"linode_grant"`
LongviewGrant types.Set `tfsdk:"longview_grant"`
NodebalancerGrant types.Set `tfsdk:"nodebalancer_grant"`
StackscriptGrant types.Set `tfsdk:"stackscript_grant"`
VolumeGrant types.Set `tfsdk:"volume_grant"`
DatabaseGrant types.Set `tfsdk:"database_grant"`
ID types.String `tfsdk:"id"`
PasswordCreated types.String `tfsdk:"password_created"`
TFAEnabled types.Bool `tfsdk:"tfa_enabled"`
VerifiedPhoneNumber types.String `tfsdk:"verified_phone_number"`
}

func (data *DataSourceModel) parseUser(
func (data *DataSourceModel) ParseUser(
ctx context.Context, user *linodego.User,
) diag.Diagnostics {
data.Username = types.StringValue(user.Username)
data.Email = types.StringValue(user.Email)
data.Restricted = types.BoolValue(user.Restricted)
data.TFAEnabled = types.BoolValue(user.TFAEnabled)
data.VerifiedPhoneNumber = types.StringPointerValue(user.VerifiedPhoneNumber)

if user.PasswordCreated != nil {
data.PasswordCreated = types.StringValue(user.PasswordCreated.Format(time.RFC3339))
} else {
data.PasswordCreated = types.StringNull()
}

sshKeys, diags := types.ListValueFrom(ctx, types.StringType, user.SSHKeys)
if diags.HasError() {
Expand All @@ -53,7 +65,7 @@ func (data *DataSourceModel) parseUser(
return nil
}

func (data *DataSourceModel) parseUserGrants(
func (data *DataSourceModel) ParseUserGrants(
ctx context.Context, userGrants *linodego.UserGrants,
) diag.Diagnostics {
// Domain
Expand Down Expand Up @@ -130,6 +142,19 @@ func (data *DataSourceModel) parseUserGrants(
return nil
}

func (data *DataSourceModel) ParseNonUserGrants() {
data.DatabaseGrant = types.SetNull(linodeUserGrantsEntityObjectType)
data.DomainGrant = types.SetNull(linodeUserGrantsEntityObjectType)
data.FirewallGrant = types.SetNull(linodeUserGrantsEntityObjectType)
data.GlobalGrants = types.ListNull(linodeUserGrantsGlobalObjectType)
data.ImageGrant = types.SetNull(linodeUserGrantsEntityObjectType)
data.LinodeGrant = types.SetNull(linodeUserGrantsEntityObjectType)
data.LongviewGrant = types.SetNull(linodeUserGrantsEntityObjectType)
data.NodebalancerGrant = types.SetNull(linodeUserGrantsEntityObjectType)
data.StackscriptGrant = types.SetNull(linodeUserGrantsEntityObjectType)
data.VolumeGrant = types.SetNull(linodeUserGrantsEntityObjectType)
}

func flattenGlobalGrants(ctx context.Context, grants linodego.GlobalUserGrants) (
*basetypes.ListValue, diag.Diagnostics,
) {
Expand Down
93 changes: 93 additions & 0 deletions linode/users/datasource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package users_test

import (
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/linode/terraform-provider-linode/linode/acceptance"
"github.com/linode/terraform-provider-linode/linode/users/tmpl"
)

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

resourceName := "data.linode_users.user"
username := acctest.RandomWithPrefix("tf-test")
email := username + "@example.com"

resource.Test(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: tmpl.DataBasic(t, username, email),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "users.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.username"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.email"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.tfa_enabled"),
),
},
},
})
}

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

resourceName := "data.linode_users.user"
username := acctest.RandomWithPrefix("tf-test")
email := username + "@example.com"

resource.Test(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: tmpl.DataClientFilter(t, username, email),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "users.#", "1"),
resource.TestCheckResourceAttr(resourceName, "users.0.email", email),
resource.TestCheckResourceAttr(resourceName, "users.0.restricted", "true"),
resource.TestCheckResourceAttr(resourceName, "users.0.global_grants.#", "1"),
resource.TestCheckNoResourceAttr(resourceName, "users.0.verified_phone_number"),
resource.TestCheckNoResourceAttr(resourceName, "users.0.password_created"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.domain_grant.#"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.firewall_grant.#"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.image_grant.#"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.linode_grant.#"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.longview_grant.#"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.nodebalancer_grant.#"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.stackscript_grant.#"),
resource.TestCheckResourceAttrSet(resourceName, "users.0.volume_grant.#"),
),
},
},
})
}

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

resourceName := "data.linode_users.user"
username := acctest.RandomWithPrefix("tf-test")
email := username + "@example.com"

resource.Test(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: tmpl.DataSubstring(t, username, email),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "users.#", "2"),
acceptance.CheckResourceAttrContains(resourceName, "users.0.username", username),
),
},
},
})
}
Loading