Remove "down" operation for migration - it is not semantically valid in
general.
This commit is contained in:
parent
9ca76b65c0
commit
48d095d66f
|
@ -39,7 +39,7 @@ import (
|
|||
const (
|
||||
// DeploymentSchemaVersionCurrent is the current version of the `Deployment` schema.
|
||||
// Any deployments newer than this version will be rejected.
|
||||
DeploymentSchemaVersionCurrent = 2
|
||||
DeploymentSchemaVersionCurrent = 1
|
||||
)
|
||||
|
||||
// VersionedCheckpoint is a version number plus a json document. The version number describes what
|
||||
|
|
|
@ -32,17 +32,3 @@ func UpToCheckpointV2(v1 apitype.CheckpointV1) apitype.CheckpointV2 {
|
|||
v2.Latest = &v2deploy
|
||||
return v2
|
||||
}
|
||||
|
||||
// DownToCheckpointV1 migrates a CheckpointV2 to a CheckpointV1
|
||||
func DownToCheckpointV1(v2 apitype.CheckpointV2) apitype.CheckpointV1 {
|
||||
var v1 apitype.CheckpointV1
|
||||
v1.Stack = v2.Stack
|
||||
v1.Config = make(config.Map)
|
||||
for key, value := range v2.Config {
|
||||
v1.Config[key] = value
|
||||
}
|
||||
|
||||
v1deploy := DownToDeploymentV1(*v2.Latest)
|
||||
v1.Latest = &v1deploy
|
||||
return v1
|
||||
}
|
||||
|
|
|
@ -42,23 +42,3 @@ func TestCheckpointV1ToV2(t *testing.T) {
|
|||
}, v2.Config)
|
||||
assert.Len(t, v2.Latest.Resources, 0)
|
||||
}
|
||||
|
||||
func TestCheckpointV2ToV1(t *testing.T) {
|
||||
v2 := apitype.CheckpointV2{
|
||||
Stack: tokens.QName("mystack"),
|
||||
Config: config.Map{
|
||||
config.MustMakeKey("foo", "number"): config.NewValue("42"),
|
||||
},
|
||||
Latest: &apitype.DeploymentV2{
|
||||
Manifest: apitype.ManifestV1{},
|
||||
Resources: []apitype.ResourceV2{},
|
||||
},
|
||||
}
|
||||
|
||||
v1 := DownToCheckpointV1(v2)
|
||||
assert.Equal(t, tokens.QName("mystack"), v1.Stack)
|
||||
assert.Equal(t, config.Map{
|
||||
config.MustMakeKey("foo", "number"): config.NewValue("42"),
|
||||
}, v1.Config)
|
||||
assert.Len(t, v1.Latest.Resources, 0)
|
||||
}
|
||||
|
|
|
@ -27,19 +27,3 @@ func UpToDeploymentV2(v1 apitype.DeploymentV1) apitype.DeploymentV2 {
|
|||
|
||||
return v2
|
||||
}
|
||||
|
||||
// DownToDeploymentV1 migrates a deployment from DeploymentV2 to DeploymentV1.
|
||||
func DownToDeploymentV1(v2 apitype.DeploymentV2) apitype.DeploymentV1 {
|
||||
var v1 apitype.DeploymentV1
|
||||
v1.Manifest = v2.Manifest
|
||||
for _, res := range v2.Resources {
|
||||
if res.External {
|
||||
// External resources were not stored in the deployment prior to V2.
|
||||
continue
|
||||
}
|
||||
|
||||
v1.Resources = append(v1.Resources, DownToResourceV1(res))
|
||||
}
|
||||
|
||||
return v1
|
||||
}
|
||||
|
|
|
@ -41,24 +41,3 @@ func TestDeploymentV1ToV2(t *testing.T) {
|
|||
assert.Equal(t, resource.URN("a"), v1.Resources[0].URN)
|
||||
assert.Equal(t, resource.URN("b"), v1.Resources[1].URN)
|
||||
}
|
||||
|
||||
func TestDeploymentV2ToV1(t *testing.T) {
|
||||
v2 := apitype.DeploymentV2{
|
||||
Manifest: apitype.ManifestV1{},
|
||||
Resources: []apitype.ResourceV2{
|
||||
{
|
||||
URN: resource.URN("a"),
|
||||
},
|
||||
{
|
||||
URN: resource.URN("b"),
|
||||
// this resource gets excluded from the V1 snapshot because it's external
|
||||
External: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
v1 := DownToDeploymentV1(v2)
|
||||
assert.Equal(t, v2.Manifest, v1.Manifest)
|
||||
assert.Len(t, v1.Resources, 1)
|
||||
assert.Equal(t, resource.URN("a"), v1.Resources[0].URN)
|
||||
}
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
// limitations under the License.
|
||||
|
||||
// Package migrate is responsible for converting to and from the various API
|
||||
// type versions that are in use in Pulumi. This package can migrate "up" and
|
||||
// "down" versions for every versioned API that needs to be migrated between
|
||||
// versions. Today, there are three versionable entities that can be migrated
|
||||
// type versions that are in use in Pulumi. This package can migrate "up" for
|
||||
// every versioned API that needs to be migrated between versions. Today, there
|
||||
// are three versionable entities that can be migrated
|
||||
// with this package:
|
||||
// * Checkpoint, the on-disk format for Fire-and-Forget stack state,
|
||||
// * Deployment, the wire format for service-managed stacks,
|
||||
// * Resource, the wire format for resources saved in deployments,
|
||||
//
|
||||
// The migrations in this package are designed to preserve semantics between
|
||||
// versions. It is always safe to migrate an entity from one version to another.
|
||||
// versions. It is always safe to migrate an entity up from one version to another.
|
||||
package migrate
|
||||
|
|
|
@ -16,7 +16,6 @@ package migrate
|
|||
|
||||
import (
|
||||
"github.com/pulumi/pulumi/pkg/apitype"
|
||||
"github.com/pulumi/pulumi/pkg/util/contract"
|
||||
)
|
||||
|
||||
// UpToResourceV2 migrates a resource from ResourceV1 to ResourceV2.
|
||||
|
@ -42,33 +41,12 @@ func UpToResourceV2(v1 apitype.ResourceV1) apitype.ResourceV2 {
|
|||
// lifecycle is not owned by Pulumi. Since all V1 resources have their lifecycles
|
||||
// owned by Pulumi, this is `false` for all V1 resources.
|
||||
v2.External = false
|
||||
v2.Dependencies = append(v1.Dependencies, v2.Dependencies...)
|
||||
v2.InitErrors = append(v1.InitErrors, v2.InitErrors...)
|
||||
// v2.Provider is a reference to a first-class provider associated with this resource.
|
||||
v2.Provider = ""
|
||||
// v2.Status is the "dirtiness" of this resource - if the engine knows that the last
|
||||
// operation against it succeeded or not.
|
||||
v2.Status = ""
|
||||
v2.Dependencies = append(v2.Dependencies, v1.Dependencies...)
|
||||
v2.InitErrors = append(v2.InitErrors, v1.InitErrors...)
|
||||
return v2
|
||||
}
|
||||
|
||||
// DownToResourceV1 migrates a resource from ResourceV2 to ResourceV1.
|
||||
func DownToResourceV1(v2 apitype.ResourceV2) apitype.ResourceV1 {
|
||||
contract.Assertf(!v2.External, "Can't convert a V2 External resource to V1")
|
||||
var v1 apitype.ResourceV1
|
||||
v1.URN = v2.URN
|
||||
v1.Custom = v2.Custom
|
||||
v1.Delete = v2.Delete
|
||||
v1.ID = v2.ID
|
||||
v1.Type = v2.Type
|
||||
v1.Inputs = make(map[string]interface{})
|
||||
for key, value := range v2.Inputs {
|
||||
v1.Inputs[key] = value
|
||||
}
|
||||
// Defaults was deprecated in v2.
|
||||
v1.Defaults = make(map[string]interface{})
|
||||
v1.Outputs = make(map[string]interface{})
|
||||
for key, value := range v2.Outputs {
|
||||
v1.Outputs[key] = value
|
||||
}
|
||||
v1.Parent = v2.Parent
|
||||
v1.Protect = v2.Protect
|
||||
v1.Dependencies = append(v1.Dependencies, v2.Dependencies...)
|
||||
v1.InitErrors = append(v1.InitErrors, v2.InitErrors...)
|
||||
return v1
|
||||
}
|
||||
|
|
|
@ -66,74 +66,6 @@ func TestV1ToV2(t *testing.T) {
|
|||
resource.URN("dep1"),
|
||||
resource.URN("dep2"),
|
||||
}, v2.Dependencies)
|
||||
}
|
||||
|
||||
func TestV2ToV1(t *testing.T) {
|
||||
v2 := apitype.ResourceV2{
|
||||
URN: resource.URN("foo"),
|
||||
Custom: true,
|
||||
Delete: true,
|
||||
ID: resource.ID("bar"),
|
||||
Type: tokens.Type("special"),
|
||||
Inputs: map[string]interface{}{
|
||||
"foo_in": "baz",
|
||||
},
|
||||
Outputs: map[string]interface{}{
|
||||
"foo_out": "out",
|
||||
},
|
||||
Parent: resource.URN("parent"),
|
||||
Protect: true,
|
||||
External: false,
|
||||
Dependencies: []resource.URN{
|
||||
resource.URN("dep1"),
|
||||
resource.URN("dep2"),
|
||||
},
|
||||
}
|
||||
|
||||
v1 := DownToResourceV1(v2)
|
||||
assert.Equal(t, resource.URN("foo"), v1.URN)
|
||||
assert.True(t, v1.Custom)
|
||||
assert.True(t, v1.Delete)
|
||||
assert.Equal(t, resource.ID("bar"), v1.ID)
|
||||
assert.Equal(t, tokens.Type("special"), v1.Type)
|
||||
assert.Equal(t, map[string]interface{}{
|
||||
"foo_in": "baz",
|
||||
}, v1.Inputs)
|
||||
assert.Equal(t, map[string]interface{}{}, v1.Defaults)
|
||||
assert.Equal(t, map[string]interface{}{
|
||||
"foo_out": "out",
|
||||
}, v1.Outputs)
|
||||
assert.Equal(t, resource.URN("parent"), v1.Parent)
|
||||
assert.True(t, v1.Protect)
|
||||
assert.Equal(t, []resource.URN{
|
||||
resource.URN("dep1"),
|
||||
resource.URN("dep2"),
|
||||
}, v2.Dependencies)
|
||||
}
|
||||
|
||||
func TestInvalidV2ToV1(t *testing.T) {
|
||||
v2 := apitype.ResourceV2{
|
||||
URN: resource.URN("foo"),
|
||||
Custom: true,
|
||||
Delete: true,
|
||||
ID: resource.ID("bar"),
|
||||
Type: tokens.Type("special"),
|
||||
Inputs: map[string]interface{}{
|
||||
"foo_in": "baz",
|
||||
},
|
||||
Outputs: map[string]interface{}{
|
||||
"foo_out": "out",
|
||||
},
|
||||
Parent: resource.URN("parent"),
|
||||
Protect: true,
|
||||
External: true,
|
||||
Dependencies: []resource.URN{
|
||||
resource.URN("dep1"),
|
||||
resource.URN("dep2"),
|
||||
},
|
||||
}
|
||||
|
||||
assert.Panics(t, func() {
|
||||
DownToResourceV1(v2)
|
||||
})
|
||||
assert.Empty(t, v2.Provider)
|
||||
assert.Empty(t, v2.Status)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/apitype"
|
||||
"github.com/pulumi/pulumi/pkg/apitype/migrate"
|
||||
"github.com/pulumi/pulumi/pkg/backend"
|
||||
"github.com/pulumi/pulumi/pkg/diag/colors"
|
||||
"github.com/pulumi/pulumi/pkg/engine"
|
||||
|
@ -35,7 +34,6 @@ import (
|
|||
"github.com/pulumi/pulumi/pkg/resource/config"
|
||||
"github.com/pulumi/pulumi/pkg/tokens"
|
||||
"github.com/pulumi/pulumi/pkg/util/contract"
|
||||
"github.com/pulumi/pulumi/pkg/util/logging"
|
||||
"github.com/pulumi/pulumi/pkg/workspace"
|
||||
)
|
||||
|
||||
|
@ -499,13 +497,10 @@ func (pc *Client) InvalidateUpdateCheckpoint(ctx context.Context, update UpdateI
|
|||
}
|
||||
|
||||
// PatchUpdateCheckpoint patches the checkpoint for the indicated update with the given contents.
|
||||
func (pc *Client) PatchUpdateCheckpoint(ctx context.Context, update UpdateIdentifier, deployment *apitype.DeploymentV2,
|
||||
func (pc *Client) PatchUpdateCheckpoint(ctx context.Context, update UpdateIdentifier, deployment *apitype.DeploymentV1,
|
||||
token string) error {
|
||||
|
||||
// TODO(pulumi/pulumi#1521): until the service can understand V2 checkpoints, downgrade to V1 before sending it.
|
||||
logging.V(7).Infof("PatchUpdateCheckpoint: downgrading V2 checkpoint to V1 to speak to service")
|
||||
v1deployment := migrate.DownToDeploymentV1(*deployment)
|
||||
rawDeployment, err := json.Marshal(v1deployment)
|
||||
rawDeployment, err := json.Marshal(deployment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ func (b *localBackend) getStack(name tokens.QName) (config.Map, *deploy.Snapshot
|
|||
}
|
||||
|
||||
// GetCheckpoint loads a checkpoint file for the given stack in this project, from the current project workspace.
|
||||
func (b *localBackend) getCheckpoint(stackName tokens.QName) (*apitype.CheckpointV2, error) {
|
||||
func (b *localBackend) getCheckpoint(stackName tokens.QName) (*apitype.CheckpointV1, error) {
|
||||
chkpath := b.stackPath(stackName)
|
||||
bytes, err := ioutil.ReadFile(chkpath)
|
||||
if err != nil {
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
)
|
||||
|
||||
func getPulumiResources(t *testing.T, path string) *Resource {
|
||||
var checkpoint apitype.CheckpointV2
|
||||
var checkpoint apitype.CheckpointV1
|
||||
byts, err := ioutil.ReadFile(path)
|
||||
assert.NoError(t, err)
|
||||
err = json.Unmarshal(byts, &checkpoint)
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/apitype"
|
||||
"github.com/pulumi/pulumi/pkg/apitype/migrate"
|
||||
"github.com/pulumi/pulumi/pkg/resource"
|
||||
"github.com/pulumi/pulumi/pkg/resource/config"
|
||||
"github.com/pulumi/pulumi/pkg/resource/deploy"
|
||||
|
@ -32,7 +31,7 @@ import (
|
|||
"github.com/pulumi/pulumi/pkg/workspace"
|
||||
)
|
||||
|
||||
func UnmarshalVersionedCheckpointToLatestCheckpoint(bytes []byte) (*apitype.CheckpointV2, error) {
|
||||
func UnmarshalVersionedCheckpointToLatestCheckpoint(bytes []byte) (*apitype.CheckpointV1, error) {
|
||||
var versionedCheckpoint apitype.VersionedCheckpoint
|
||||
if err := json.Unmarshal(bytes, &versionedCheckpoint); err != nil {
|
||||
return nil, err
|
||||
|
@ -49,22 +48,13 @@ func UnmarshalVersionedCheckpointToLatestCheckpoint(bytes []byte) (*apitype.Chec
|
|||
return nil, err
|
||||
}
|
||||
|
||||
v2checkpoint := migrate.UpToCheckpointV2(checkpoint)
|
||||
return &v2checkpoint, nil
|
||||
return &checkpoint, nil
|
||||
case 1:
|
||||
var checkpoint apitype.CheckpointV1
|
||||
if err := json.Unmarshal(versionedCheckpoint.Checkpoint, &checkpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v2checkpoint := migrate.UpToCheckpointV2(checkpoint)
|
||||
return &v2checkpoint, nil
|
||||
case 2:
|
||||
var checkpoint apitype.CheckpointV2
|
||||
if err := json.Unmarshal(versionedCheckpoint.Checkpoint, &checkpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &checkpoint, nil
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported checkpoint version %d", versionedCheckpoint.Version)
|
||||
|
@ -74,12 +64,12 @@ func UnmarshalVersionedCheckpointToLatestCheckpoint(bytes []byte) (*apitype.Chec
|
|||
// SerializeCheckpoint turns a snapshot into a data structure suitable for serialization.
|
||||
func SerializeCheckpoint(stack tokens.QName, config config.Map, snap *deploy.Snapshot) *apitype.VersionedCheckpoint {
|
||||
// If snap is nil, that's okay, we will just create an empty deployment; otherwise, serialize the whole snapshot.
|
||||
var latest *apitype.DeploymentV2
|
||||
var latest *apitype.DeploymentV1
|
||||
if snap != nil {
|
||||
latest = SerializeDeployment(snap)
|
||||
}
|
||||
|
||||
b, err := json.Marshal(apitype.CheckpointV2{
|
||||
b, err := json.Marshal(apitype.CheckpointV1{
|
||||
Stack: stack,
|
||||
Config: config,
|
||||
Latest: latest,
|
||||
|
@ -93,7 +83,7 @@ func SerializeCheckpoint(stack tokens.QName, config config.Map, snap *deploy.Sna
|
|||
}
|
||||
|
||||
// DeserializeCheckpoint takes a serialized deployment record and returns its associated snapshot.
|
||||
func DeserializeCheckpoint(chkpoint *apitype.CheckpointV2) (*deploy.Snapshot, error) {
|
||||
func DeserializeCheckpoint(chkpoint *apitype.CheckpointV1) (*deploy.Snapshot, error) {
|
||||
contract.Require(chkpoint != nil, "chkpoint")
|
||||
|
||||
var snap *deploy.Snapshot
|
||||
|
|
|
@ -20,11 +20,9 @@ import (
|
|||
"reflect"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/apitype"
|
||||
"github.com/pulumi/pulumi/pkg/apitype/migrate"
|
||||
"github.com/pulumi/pulumi/pkg/resource"
|
||||
"github.com/pulumi/pulumi/pkg/resource/deploy"
|
||||
"github.com/pulumi/pulumi/pkg/util/contract"
|
||||
"github.com/pulumi/pulumi/pkg/util/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -46,7 +44,7 @@ var (
|
|||
)
|
||||
|
||||
// SerializeDeployment serializes an entire snapshot as a deploy record.
|
||||
func SerializeDeployment(snap *deploy.Snapshot) *apitype.DeploymentV2 {
|
||||
func SerializeDeployment(snap *deploy.Snapshot) *apitype.DeploymentV1 {
|
||||
contract.Require(snap != nil, "snap")
|
||||
|
||||
// Capture the version information into a manifest.
|
||||
|
@ -69,12 +67,12 @@ func SerializeDeployment(snap *deploy.Snapshot) *apitype.DeploymentV2 {
|
|||
}
|
||||
|
||||
// Serialize all vertices and only include a vertex section if non-empty.
|
||||
var resources []apitype.ResourceV2
|
||||
var resources []apitype.ResourceV1
|
||||
for _, res := range snap.Resources {
|
||||
resources = append(resources, SerializeResource(res))
|
||||
}
|
||||
|
||||
return &apitype.DeploymentV2{
|
||||
return &apitype.DeploymentV1{
|
||||
Manifest: manifest,
|
||||
Resources: resources,
|
||||
}
|
||||
|
@ -92,30 +90,19 @@ func DeserializeDeployment(deployment *apitype.UntypedDeployment) (*deploy.Snaps
|
|||
return nil, ErrDeploymentSchemaVersionTooOld
|
||||
}
|
||||
|
||||
var checkpoint apitype.CheckpointV2
|
||||
var checkpoint apitype.CheckpointV1
|
||||
switch deployment.Version {
|
||||
case 1:
|
||||
v1checkpoint := apitype.CheckpointV1{}
|
||||
if err := json.Unmarshal([]byte(deployment.Deployment), &v1checkpoint.Latest); err != nil {
|
||||
if err := json.Unmarshal([]byte(deployment.Deployment), &checkpoint.Latest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logging.V(7).Infof("DeserializeDeployment: migrating V1 checkpoint to V2")
|
||||
checkpoint = migrate.UpToCheckpointV2(v1checkpoint)
|
||||
case 2:
|
||||
v2checkpoint := apitype.CheckpointV2{}
|
||||
if err := json.Unmarshal([]byte(deployment.Deployment), &v2checkpoint.Latest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
checkpoint = v2checkpoint
|
||||
}
|
||||
|
||||
return DeserializeCheckpoint(&checkpoint)
|
||||
}
|
||||
|
||||
// SerializeResource turns a resource into a structure suitable for serialization.
|
||||
func SerializeResource(res *resource.State) apitype.ResourceV2 {
|
||||
func SerializeResource(res *resource.State) apitype.ResourceV1 {
|
||||
contract.Assert(res != nil)
|
||||
contract.Assertf(string(res.URN) != "", "Unexpected empty resource resource.URN")
|
||||
|
||||
|
@ -129,7 +116,7 @@ func SerializeResource(res *resource.State) apitype.ResourceV2 {
|
|||
outputs = SerializeProperties(outp)
|
||||
}
|
||||
|
||||
return apitype.ResourceV2{
|
||||
return apitype.ResourceV1{
|
||||
URN: res.URN,
|
||||
Custom: res.Custom,
|
||||
Delete: res.Delete,
|
||||
|
@ -190,7 +177,7 @@ func SerializePropertyValue(prop resource.PropertyValue) interface{} {
|
|||
}
|
||||
|
||||
// DeserializeResource turns a serialized resource back into its usual form.
|
||||
func DeserializeResource(res apitype.ResourceV2) (*resource.State, error) {
|
||||
func DeserializeResource(res apitype.ResourceV1) (*resource.State, error) {
|
||||
// Deserialize the resource properties, if they exist.
|
||||
inputs, err := DeserializeProperties(res.Inputs)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in a new issue