pulumi/cmd/lumi/provider_env.go

122 lines
4.1 KiB
Go

// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
package main
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/pulumi/pulumi-fabric/pkg/encoding"
"github.com/pulumi/pulumi-fabric/pkg/resource/deploy"
"github.com/pulumi/pulumi-fabric/pkg/resource/environment"
"github.com/pulumi/pulumi-fabric/pkg/tokens"
"github.com/pulumi/pulumi-fabric/pkg/util/contract"
"github.com/pulumi/pulumi-fabric/pkg/util/mapper"
"github.com/pulumi/pulumi-fabric/pkg/workspace"
)
type localEnvProvider struct{}
func (p localEnvProvider) GetEnvironment(name tokens.QName) (*deploy.Target,
*deploy.Snapshot, *environment.Checkpoint, error) {
contract.Require(name != "", "name")
file := workspace.EnvPath(name)
// Detect the encoding of the file so we can do our initial unmarshaling.
m, ext := encoding.Detect(file)
if m == nil {
return nil, nil, nil, errors.Errorf("resource deserialization failed; illegal markup extension: '%v'", ext)
}
// Now read the whole file into a byte blob.
b, err := ioutil.ReadFile(file)
if err != nil {
if os.IsNotExist(err) {
return nil, nil, nil, errors.Errorf("Environment '%v' could not be found in the current workspace", name)
}
return nil, nil, nil, errors.Wrapf(err, "An IO error occurred during the current operation")
}
// Unmarshal the contents into a checkpoint structure.
var checkpoint environment.Checkpoint
if err = m.Unmarshal(b, &checkpoint); err != nil {
return nil, nil, nil, errors.Wrapf(err, "Could not read deployment file '%v'", file)
}
// Next, use the mapping infrastructure to validate the contents.
// IDEA: we can eliminate this redundant unmarshaling once Go supports strict unmarshaling.
var obj map[string]interface{}
if err = m.Unmarshal(b, &obj); err != nil {
return nil, nil, nil, errors.Wrapf(err, "Could not read deployment file '%v'", file)
}
if obj["latest"] != nil {
if latest, islatest := obj["latest"].(map[string]interface{}); islatest {
delete(latest, "resources") // remove the resources, since they require custom marshaling.
}
}
md := mapper.New(nil)
var ignore environment.Checkpoint // just for errors.
if err = md.Decode(obj, &ignore); err != nil {
return nil, nil, nil, errors.Wrapf(err, "Could not read deployment file '%v'", file)
}
target, snapshot := environment.DeserializeCheckpoint(&checkpoint)
contract.Assert(target != nil)
return target, snapshot, &checkpoint, nil
}
func (p localEnvProvider) SaveEnvironment(env *deploy.Target, snap *deploy.Snapshot) error {
contract.Require(env != nil, "env")
file := workspace.EnvPath(env.Name)
// Make a serializable LumiGL data structure and then use the encoder to encode it.
m, ext := encoding.Detect(file)
if m == nil {
return errors.Errorf("resource serialization failed; illegal markup extension: '%v'", ext)
}
if filepath.Ext(file) == "" {
file = file + ext
}
dep := environment.SerializeCheckpoint(env, snap)
b, err := m.Marshal(dep)
if err != nil {
return errors.Wrap(err, "An IO error occurred during the current operation")
}
// Back up the existing file if it already exists.
backupTarget(file)
// Ensure the directory exists.
if err = os.MkdirAll(filepath.Dir(file), 0700); err != nil {
return errors.Wrap(err, "An IO error occurred during the current operation")
}
// And now write out the new snapshot file, overwriting that location.
if err = ioutil.WriteFile(file, b, 0600); err != nil {
return errors.Wrap(err, "An IO error occurred during the current operation")
}
return nil
}
func (p localEnvProvider) RemoveEnvironment(env *deploy.Target) error {
contract.Require(env != nil, "env")
// Just make a backup of the file and don't write out anything new.
file := workspace.EnvPath(env.Name)
backupTarget(file)
return nil
}
// backupTarget makes a backup of an existing file, in preparation for writing a new one. Instead of a copy, it
// simply renames the file, which is simpler, more efficient, etc.
func backupTarget(file string) {
contract.Require(file != "", "file")
err := os.Rename(file, file+".bak")
contract.IgnoreError(err) // ignore errors.
// IDEA: consider multiple backups (.bak.bak.bak...etc).
}