github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/stack/checkpoint.go (about) 1 // Copyright 2016-2022, Pulumi Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package stack 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 22 "github.com/pulumi/pulumi/pkg/v3/resource/deploy" 23 "github.com/pulumi/pulumi/pkg/v3/secrets" 24 "github.com/pulumi/pulumi/sdk/v3/go/common/apitype" 25 "github.com/pulumi/pulumi/sdk/v3/go/common/apitype/migrate" 26 "github.com/pulumi/pulumi/sdk/v3/go/common/encoding" 27 "github.com/pulumi/pulumi/sdk/v3/go/common/resource" 28 "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" 29 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 30 ) 31 32 func UnmarshalVersionedCheckpointToLatestCheckpoint(m encoding.Marshaler, bytes []byte) (*apitype.CheckpointV3, error) { 33 var versionedCheckpoint apitype.VersionedCheckpoint 34 // Here we are careful to unmarshal `bytes` with the provided unmarshaller `m`. 35 if err := m.Unmarshal(bytes, &versionedCheckpoint); err != nil { 36 return nil, fmt.Errorf("place 1: %w", err) 37 } 38 39 switch versionedCheckpoint.Version { 40 case 0: 41 // The happens when we are loading a checkpoint file from before we started to version things. Go's 42 // json package did not support strict marshalling before 1.10, and we use 1.9 in our toolchain today. 43 // After we upgrade, we could consider rewriting this code to use DisallowUnknownFields() on the decoder 44 // to have the old checkpoint not even deserialize as an apitype.VersionedCheckpoint. 45 var v1checkpoint apitype.CheckpointV1 46 if err := m.Unmarshal(bytes, &v1checkpoint); err != nil { 47 return nil, err 48 } 49 50 v2checkpoint := migrate.UpToCheckpointV2(v1checkpoint) 51 v3checkpoint := migrate.UpToCheckpointV3(v2checkpoint) 52 return &v3checkpoint, nil 53 case 1: 54 var v1checkpoint apitype.CheckpointV1 55 if err := json.Unmarshal(versionedCheckpoint.Checkpoint, &v1checkpoint); err != nil { 56 return nil, err 57 } 58 59 v2checkpoint := migrate.UpToCheckpointV2(v1checkpoint) 60 v3checkpoint := migrate.UpToCheckpointV3(v2checkpoint) 61 return &v3checkpoint, nil 62 case 2: 63 var v2checkpoint apitype.CheckpointV2 64 if err := json.Unmarshal(versionedCheckpoint.Checkpoint, &v2checkpoint); err != nil { 65 return nil, err 66 } 67 68 v3checkpoint := migrate.UpToCheckpointV3(v2checkpoint) 69 return &v3checkpoint, nil 70 case 3: 71 var v3checkpoint apitype.CheckpointV3 72 if err := json.Unmarshal(versionedCheckpoint.Checkpoint, &v3checkpoint); err != nil { 73 return nil, err 74 } 75 76 return &v3checkpoint, nil 77 default: 78 return nil, fmt.Errorf("unsupported checkpoint version %d", versionedCheckpoint.Version) 79 } 80 } 81 82 // SerializeCheckpoint turns a snapshot into a data structure suitable for serialization. 83 func SerializeCheckpoint(stack tokens.Name, snap *deploy.Snapshot, 84 sm secrets.Manager, showSecrets bool) (*apitype.VersionedCheckpoint, error) { 85 // If snap is nil, that's okay, we will just create an empty deployment; otherwise, serialize the whole snapshot. 86 var latest *apitype.DeploymentV3 87 if snap != nil { 88 dep, err := SerializeDeployment(snap, sm, showSecrets) 89 if err != nil { 90 return nil, fmt.Errorf("serializing deployment: %w", err) 91 } 92 latest = dep 93 } 94 95 b, err := encoding.JSON.Marshal(apitype.CheckpointV3{ 96 Stack: stack.Q(), 97 Latest: latest, 98 }) 99 if err != nil { 100 return nil, fmt.Errorf("marshalling checkpoint: %w", err) 101 } 102 103 return &apitype.VersionedCheckpoint{ 104 Version: apitype.DeploymentSchemaVersionCurrent, 105 Checkpoint: json.RawMessage(b), 106 }, nil 107 } 108 109 // DeserializeCheckpoint takes a serialized deployment record and returns its associated snapshot. Returns nil 110 // if there have been no deployments performed on this checkpoint. 111 func DeserializeCheckpoint( 112 ctx context.Context, 113 chkpoint *apitype.CheckpointV3) (*deploy.Snapshot, error) { 114 contract.Require(chkpoint != nil, "chkpoint") 115 if chkpoint.Latest != nil { 116 return DeserializeDeploymentV3(ctx, *chkpoint.Latest, DefaultSecretsProvider) 117 } 118 119 return nil, nil 120 } 121 122 // GetRootStackResource returns the root stack resource from a given snapshot, or nil if not found. 123 func GetRootStackResource(snap *deploy.Snapshot) (*resource.State, error) { 124 if snap != nil { 125 for _, res := range snap.Resources { 126 if res.Type == resource.RootStackType { 127 return res, nil 128 } 129 } 130 } 131 return nil, nil 132 }