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  }