Skip to content

Commit

Permalink
Support Read for TeamStackPermission (#205)
Browse files Browse the repository at this point in the history
Fixes #202
  • Loading branch information
komalali authored Dec 18, 2023
1 parent 5a0bcd5 commit af0aab1
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CHANGELOG
=========
6 changes: 6 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### Improvements

### Bug Fixes

- Fix `Read` for TeamStackPermission so resources are not deleted from state on refresh. Note: TeamStackPermission resources created before v0.17.0 will now return an error if attempting a refresh, but those (re)created with the new version will support `refresh`. [#205](https://github.com/pulumi/pulumi-pulumiservice/pull/205)

3 changes: 1 addition & 2 deletions examples/examples_yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,7 @@ func TestYamlOrgAccessTokenExample(t *testing.T) {
func TestYamlTeamStackPermissionsExample(t *testing.T) {
cwd, _ := os.Getwd()
integration.ProgramTest(t, &integration.ProgramTestOptions{
Quick: true,
SkipRefresh: true,
Quick: true,
// Name is specified in yaml-team-stack-permissions/Pulumi.yaml, so this has to be consistent
StackName: "dev",
Dir: path.Join(cwd, ".", "yaml-team-stack-permissions"),
Expand Down
49 changes: 44 additions & 5 deletions provider/pkg/internal/pulumiapi/teams.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ type Teams struct {
}

type Team struct {
Type string `json:"kind"`
Name string
DisplayName string
Description string
Members []TeamMember
Type string `json:"kind"`
Name string
DisplayName string
Description string
Members []TeamMember
Stacks []TeamStackPermission
Environments []TeamEnvironmentPermission
}

type TeamMember struct {
Expand All @@ -52,6 +54,17 @@ type TeamMember struct {
Role string
}

type TeamStackPermission struct {
ProjectName string `json:"projectName"`
StackName string `json:"stackName"`
Permission int `json:"permission"`
}

type TeamEnvironmentPermission struct {
EnvName string `json:"envName"`
Permission string `json:"permission"`
}

type createTeamRequest struct {
Organization string `json:"organization"`
TeamType string `json:"teamType"`
Expand Down Expand Up @@ -307,3 +320,29 @@ func (c *Client) RemoveStackPermission(ctx context.Context, stack StackName, tea
}
return nil
}

func (c *Client) GetTeamStackPermission(ctx context.Context, stack StackName, teamName string) (*int, error) {
if len(stack.OrgName) == 0 {
return nil, errors.New("orgname must not be empty")
}

if len(teamName) == 0 {
return nil, errors.New("teamname must not be empty")
}

apiPath := path.Join("orgs", stack.OrgName, "teams", teamName)

var team Team
_, err := c.do(ctx, http.MethodGet, apiPath, nil, &team)
if err != nil {
return nil, fmt.Errorf("failed to get team: %w", err)
}

for _, stackPermission := range team.Stacks {
if stackPermission.ProjectName == stack.ProjectName && stackPermission.StackName == stack.StackName {
return &stackPermission.Permission, nil
}
}

return nil, nil
}
81 changes: 74 additions & 7 deletions provider/pkg/provider/team_stack_perm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package provider

import (
"context"
"fmt"
"strings"

"github.com/google/uuid"
pbempty "google.golang.org/protobuf/types/known/emptypb"

"github.com/pulumi/pulumi-pulumiservice/provider/pkg/internal/pulumiapi"
"github.com/pulumi/pulumi-pulumiservice/provider/pkg/internal/serde"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
)

Expand Down Expand Up @@ -43,12 +45,55 @@ func (tp *TeamStackPermissionResource) Check(req *pulumirpc.CheckRequest) (*pulu
}, nil
}

func (tp *TeamStackPermissionResource) Configure(config PulumiServiceConfig) {
func (tp *TeamStackPermissionResource) Configure(_ PulumiServiceConfig) {

}

func (tp *TeamStackPermissionResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) {
return &pulumirpc.ReadResponse{}, nil
ctx := context.Background()
id := req.GetId()

permId, err := splitTeamStackPermissionId(id)
if err != nil {
if strings.Contains(err.Error(), "expected 4 parts") {
// Return an error if attempting to refresh stack permissions created before this change.
// We return a warning and an empty response, which will cause the resource to be deleted on refresh,
// forcing the user to recreate it with the updated version.
return nil, fmt.Errorf("TeamStackPermission resources created before v0.17.0 do not support refresh. " +
"You will need to destroy and recreate this resource with >v0.17.0 to successfully refresh.")
}
return nil, err
}

permission, err := tp.client.GetTeamStackPermission(ctx, pulumiapi.StackName{
OrgName: permId.Organization,
ProjectName: permId.Project,
StackName: permId.Stack,
}, permId.Team)
if err != nil {
return nil, fmt.Errorf("failed to get team stack permission: %w", err)
}
if permission == nil {
return &pulumirpc.ReadResponse{}, nil
}

inputs := TeamStackPermissionInput{
Organization: permId.Organization,
Project: permId.Project,
Stack: permId.Stack,
Team: permId.Team,
Permission: *permission,
}

properties, err := plugin.MarshalProperties(inputs.ToPropertyMap(), plugin.MarshalOptions{})
if err != nil {
return nil, fmt.Errorf("failed to marshal inputs to properties: %w", err)
}
return &pulumirpc.ReadResponse{
Id: req.Id,
Properties: properties,
Inputs: properties,
}, nil
}

func (tp *TeamStackPermissionResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) {
Expand All @@ -69,8 +114,10 @@ func (tp *TeamStackPermissionResource) Create(req *pulumirpc.CreateRequest) (*pu
return nil, err
}

stackPermissionId := fmt.Sprintf("%s/%s", stackName.String(), inputs.Team)

return &pulumirpc.CreateResponse{
Id: uuid.NewString(),
Id: stackPermissionId,
Properties: req.GetProperties(),
}, nil
}
Expand Down Expand Up @@ -109,7 +156,27 @@ func (tp *TeamStackPermissionResource) Diff(req *pulumirpc.DiffRequest) (*pulumi
}, nil
}

// Update does nothing because we always do a replace on changes, never an update
func (tp *TeamStackPermissionResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) {
return &pulumirpc.UpdateResponse{}, nil
// Update does nothing because we always replace on changes, never an update
func (tp *TeamStackPermissionResource) Update(_ *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) {
return nil, fmt.Errorf("unexpected call to update, expected create to be called instead")
}

type teamStackPermissionId struct {
Organization string
Project string
Stack string
Team string
}

func splitTeamStackPermissionId(id string) (teamStackPermissionId, error) {
split := strings.Split(id, "/")
if len(split) != 4 {
return teamStackPermissionId{}, fmt.Errorf("invalid id %q, expected 4 parts", id)
}
return teamStackPermissionId{
Organization: split[0],
Project: split[1],
Stack: split[2],
Team: split[3],
}, nil
}

0 comments on commit af0aab1

Please sign in to comment.