Skip to content

Commit

Permalink
Adding Environment Resource
Browse files Browse the repository at this point in the history
  • Loading branch information
IaroslavTitov committed May 15, 2024
1 parent d2b7276 commit 69a85eb
Show file tree
Hide file tree
Showing 16 changed files with 1,154 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
### Improvements

- Added support for Environment resource [#255](https://github.com/pulumi/pulumi-pulumiservice/issues/255)

### Bug Fixes

- Prevent noisy drift if agentPoolId is empty [#268](https://github.com/pulumi/pulumi-pulumiservice/issues/268)
Expand Down
41 changes: 41 additions & 0 deletions provider/cmd/pulumi-resource-pulumiservice/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,47 @@
"stack",
"timestamp"
]
},
"pulumiservice:index:Environment": {
"description": "An ESC Environment.",
"properties": {
"organization": {
"description": "Organization name.",
"type": "string"
},
"name": {
"description": "Environment name.",
"type": "string"
},
"yaml": {
"description": "Environment's yaml file.",
"$ref": "pulumi.json#/Asset"
}
},
"required": [
"organization",
"name",
"yaml"
],
"inputProperties": {
"organization": {
"description": "Organization name.",
"type": "string"
},
"name": {
"description": "Environment name.",
"type": "string"
},
"yaml": {
"description": "Environment's yaml file.",
"$ref": "pulumi.json#/Asset"
}
},
"requiredInputs": [
"organization",
"name",
"yaml"
]
}
},
"language": {
Expand Down
3 changes: 2 additions & 1 deletion provider/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
Expand Down Expand Up @@ -66,7 +67,7 @@ require (
github.com/pkg/term v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
github.com/pulumi/esc v0.6.2 // indirect
github.com/pulumi/esc v0.8.3 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
Expand Down
7 changes: 5 additions & 2 deletions provider/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
Expand Down Expand Up @@ -154,8 +157,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
github.com/pulumi/esc v0.6.2 h1:+z+l8cuwIauLSwXQS0uoI3rqB+YG4SzsZYtHfNoXBvw=
github.com/pulumi/esc v0.6.2/go.mod h1:jNnYNjzsOgVTjCp0LL24NsCk8ZJxq4IoLQdCT0X7l8k=
github.com/pulumi/esc v0.8.3 h1:myeDL6dD/mz34zZjCL8s7d/tWHBJYxfMxDCL11MHoqc=
github.com/pulumi/esc v0.8.3/go.mod h1:v5VAPxYDa9DRwvubbzKt4ZYf5y0esWC2ccSp/AT923I=
github.com/pulumi/pulumi/pkg/v3 v3.112.0 h1:vhoM6sx1eegJntIeUZENtck3VeMtK1zBiQ2E3EPOnHw=
github.com/pulumi/pulumi/pkg/v3 v3.112.0/go.mod h1:GQhNr0v5E8TACF8j0p6UQqyr7mZreUpoMfVjLeu6eY0=
github.com/pulumi/pulumi/sdk/v3 v3.115.3-0.20240507143413-cffdfd1fa489 h1:e7J2I8veUe9mSpzWN9kPREp7YhHUwnQz0aP0k5R45V8=
Expand Down
269 changes: 269 additions & 0 deletions provider/pkg/provider/environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
package provider

import (
"context"
"fmt"
"path"
"strings"

esc_client "github.com/pulumi/esc/cmd/esc/cli/client"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/asset"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
pbempty "google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/structpb"
)

type PulumiServiceEnvironmentResource struct {
client esc_client.Client
}

type PulumiServiceEnvironmentInput struct {
OrgName string
EnvName string
Yaml []byte
}

func (i *PulumiServiceEnvironmentInput) ToPropertyMap() (resource.PropertyMap, error) {
propertyMap := resource.PropertyMap{}
propertyMap["organization"] = resource.NewPropertyValue(i.OrgName)
propertyMap["name"] = resource.NewPropertyValue(i.EnvName)

yamlAsset, err := asset.FromText(strings.TrimSuffix(string(i.Yaml), "\n"))
if err != nil {
return nil, err
}
propertyMap["yaml"] = resource.NewAssetProperty(yamlAsset)

return propertyMap, nil
}

func ToPulumiServiceEnvironmentInput(properties *structpb.Struct) (*PulumiServiceEnvironmentInput, error) {
inputMap, err := plugin.UnmarshalProperties(properties, plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true})
if err != nil {
return nil, err
}

input := PulumiServiceEnvironmentInput{}
if inputMap["organization"].HasValue() && inputMap["organization"].IsString() {
input.OrgName = inputMap["organization"].StringValue()
} else {
return nil, fmt.Errorf("failed to unmarshal organization value from properties: %s", inputMap)
}
if inputMap["name"].HasValue() && inputMap["name"].IsString() {
input.EnvName = inputMap["name"].StringValue()
} else {
return nil, fmt.Errorf("failed to unmarshal environment name value from properties: %s", inputMap)
}
if inputMap["yaml"].HasValue() && inputMap["yaml"].IsAsset() {
input.Yaml = []byte(inputMap["yaml"].AssetValue().Text)
} else {
return nil, fmt.Errorf("failed to unmarshal yaml value from properties: %s", inputMap)
}

return &input, nil
}

func (st *PulumiServiceEnvironmentResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) {
olds, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{})
if err != nil {
return nil, err
}

news, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{})
if err != nil {
return nil, err
}

diffs := olds.Diff(news)
if diffs == nil {
return &pulumirpc.DiffResponse{
Changes: pulumirpc.DiffResponse_DIFF_NONE,
}, nil
}

dd := plugin.NewDetailedDiffFromObjectDiff(diffs, false)

detailedDiffs := map[string]*pulumirpc.PropertyDiff{}
replaces := []string(nil)
replaceProperties := map[string]bool{
"organization": true,
"name": true,
}
for k, v := range dd {
if _, ok := replaceProperties[k]; ok {
v.Kind = v.Kind.AsReplace()
replaces = append(replaces, k)
}
detailedDiffs[k] = &pulumirpc.PropertyDiff{
Kind: pulumirpc.PropertyDiff_Kind(v.Kind),
InputDiff: v.InputDiff,
}
}

changes := pulumirpc.DiffResponse_DIFF_NONE
if len(detailedDiffs) > 0 {
changes = pulumirpc.DiffResponse_DIFF_SOME
}
return &pulumirpc.DiffResponse{
Changes: changes,
Replaces: replaces,
DetailedDiff: detailedDiffs,
HasDetailedDiff: true,
DeleteBeforeReplace: len(replaces) > 0,
}, nil
}

func (st *PulumiServiceEnvironmentResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) {
input, err := ToPulumiServiceEnvironmentInput(req.GetProperties())
if err != nil {
return nil, err
}

err = st.client.DeleteEnvironment(context.Background(), input.OrgName, input.EnvName)
if err != nil {
return nil, err
}
return &pbempty.Empty{}, nil
}

func (st *PulumiServiceEnvironmentResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) {
input, err := ToPulumiServiceEnvironmentInput(req.GetProperties())
if err != nil {
return nil, err
}

// First check if yaml is valid
_, diagnostics, err := st.client.CheckYAMLEnvironment(context.Background(), input.OrgName, input.Yaml)
if err != nil {
return nil, err
}
if diagnostics != nil {
return nil, fmt.Errorf("failed to create environment, yaml code failed following checks: %+v", diagnostics)
}

// Then create environment, and update it with yaml provided. ESC API architecture doesn't let you do it in one call
err = st.client.CreateEnvironment(context.Background(), input.OrgName, input.EnvName)
if err != nil {
return nil, err
}
diagnostics, err = st.client.UpdateEnvironment(context.Background(), input.OrgName, input.EnvName, input.Yaml, "")
if err != nil {
return nil, err
}
if diagnostics != nil {
return nil, fmt.Errorf("failed to update brand new environment with pre-checked yaml, due to failing the following checks: %+v \n"+
"This should never happen, if you're seeing this message there's likely a bug in ESC APIs", diagnostics)
}

propertyMap, err := input.ToPropertyMap()
if err != nil {
return nil, err
}
outputProperties, err := plugin.MarshalProperties(
propertyMap,
plugin.MarshalOptions{},
)
if err != nil {
return nil, err
}

return &pulumirpc.CreateResponse{
Id: path.Join(input.OrgName, input.EnvName),
Properties: outputProperties,
}, nil
}

func (st *PulumiServiceEnvironmentResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) {
inputMap, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{})
if err != nil {
return nil, err
}

var failures []*pulumirpc.CheckFailure
for _, p := range []resource.PropertyKey{"organization", "name", "yaml"} {
if !inputMap[(p)].HasValue() {
failures = append(failures, &pulumirpc.CheckFailure{
Reason: fmt.Sprintf("missing required property '%s'", p),
Property: string(p),
})
}
}

return &pulumirpc.CheckResponse{Inputs: req.GetNews(), Failures: failures}, nil
}

func (st *PulumiServiceEnvironmentResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) {
input, err := ToPulumiServiceEnvironmentInput(req.GetNews())
if err != nil {
return nil, err
}

diagnostics, err := st.client.UpdateEnvironment(context.Background(), input.OrgName, input.EnvName, input.Yaml, "")
if err != nil {
return nil, err
}
if diagnostics != nil {
return nil, fmt.Errorf("failed to update environment, yaml code failed following checks: %+v", diagnostics)
}

propertyMap, err := input.ToPropertyMap()
if err != nil {
return nil, err
}
outputProperties, err := plugin.MarshalProperties(
propertyMap,
plugin.MarshalOptions{},
)
if err != nil {
return nil, err
}

return &pulumirpc.UpdateResponse{
Properties: outputProperties,
}, nil
}

func (st *PulumiServiceEnvironmentResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) {
input, err := ToPulumiServiceEnvironmentInput(req.GetProperties())
if err != nil {
return nil, err
}

retrievedYaml, _, err := st.client.GetEnvironment(context.Background(), input.OrgName, input.EnvName, false)
if err != nil {
return &pulumirpc.ReadResponse{Id: "", Properties: nil}, nil
}

result := PulumiServiceEnvironmentInput{
OrgName: input.OrgName,
EnvName: input.EnvName,
Yaml: retrievedYaml,
}

propertyMap, err := result.ToPropertyMap()
if err != nil {
return nil, err
}
properties, err := plugin.MarshalProperties(
propertyMap,
plugin.MarshalOptions{},
)
if err != nil {
return nil, err
}

return &pulumirpc.ReadResponse{
Id: req.Id,
Properties: properties,
Inputs: properties,
}, nil
}

func (st *PulumiServiceEnvironmentResource) Name() string {
return "pulumiservice:index:Environment"
}

func (st *PulumiServiceEnvironmentResource) Configure(_ PulumiServiceConfig) {
}
Loading

0 comments on commit 69a85eb

Please sign in to comment.