Do not use os
package in filestate backend
Since the filestate backend is now written using the go-cloud blob abstraction, places where we were using functions from the `os` package were very unlikely to be correct. This change removes their uses in favor of APIs provided by go-cloud (which sometimes requires more work than before).
This commit is contained in:
parent
828086d638
commit
b14e3d9c35
|
@ -32,6 +32,7 @@ import (
|
|||
_ "gocloud.dev/blob/fileblob" // driver for file://
|
||||
_ "gocloud.dev/blob/gcsblob" // driver for gs://
|
||||
_ "gocloud.dev/blob/s3blob" // driver for s3://
|
||||
"gocloud.dev/gcerrors"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/apitype"
|
||||
"github.com/pulumi/pulumi/pkg/backend"
|
||||
|
@ -252,7 +253,7 @@ func (b *localBackend) GetStack(ctx context.Context, stackRef backend.StackRefer
|
|||
stackName := stackRef.Name()
|
||||
snapshot, path, err := b.getStack(stackName)
|
||||
switch {
|
||||
case os.IsNotExist(errors.Cause(err)):
|
||||
case gcerrors.Code(errors.Cause(err)) == gcerrors.NotFound:
|
||||
return nil, nil
|
||||
case err != nil:
|
||||
return nil, err
|
||||
|
@ -305,12 +306,13 @@ func (b *localBackend) RenameStack(ctx context.Context, stackRef backend.StackRe
|
|||
}
|
||||
|
||||
// Ensure the destination stack does not already exist.
|
||||
_, err = os.Stat(b.stackPath(newName))
|
||||
if err == nil {
|
||||
return errors.Errorf("a stack named %s already exists", newName)
|
||||
} else if !os.IsNotExist(err) {
|
||||
hasExisting, err := b.bucket.Exists(ctx, b.stackPath(newName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasExisting {
|
||||
return errors.Errorf("a stack named %s already exists", newName)
|
||||
}
|
||||
|
||||
// If we have a snapshot, we need to rename the URNs inside it to use the new stack name.
|
||||
if snap != nil {
|
||||
|
@ -328,16 +330,8 @@ func (b *localBackend) RenameStack(ctx context.Context, stackRef backend.StackRe
|
|||
file := b.stackPath(stackName)
|
||||
backupTarget(b.bucket, file)
|
||||
|
||||
// And move the history over as well, if it exists.
|
||||
oldHistoryDir := b.historyDirectory(stackName)
|
||||
if _, err := os.Stat(oldHistoryDir); err == nil {
|
||||
newHistoryDir := b.historyDirectory(newName)
|
||||
if err := os.Rename(oldHistoryDir, newHistoryDir); err != nil {
|
||||
return errors.Wrap(err, "renaming history")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// And rename the histoy folder as well.
|
||||
return b.renameHistory(stackName, newName)
|
||||
}
|
||||
|
||||
func (b *localBackend) GetLatestConfiguration(ctx context.Context,
|
||||
|
|
|
@ -20,6 +20,7 @@ type Bucket interface {
|
|||
SignedURL(ctx context.Context, key string, opts *blob.SignedURLOptions) (string, error)
|
||||
ReadAll(ctx context.Context, key string) (_ []byte, err error)
|
||||
WriteAll(ctx context.Context, key string, p []byte, opts *blob.WriterOptions) (err error)
|
||||
Exists(ctx context.Context, key string) (bool, error)
|
||||
}
|
||||
|
||||
// wrappedBucket encapsulates a true gocloud blob.Bucket, but ensures that all paths we send to it
|
||||
|
@ -54,6 +55,10 @@ func (b *wrappedBucket) WriteAll(ctx context.Context, key string, p []byte, opts
|
|||
return b.bucket.WriteAll(ctx, filepath.ToSlash(key), p, opts)
|
||||
}
|
||||
|
||||
func (b *wrappedBucket) Exists(ctx context.Context, key string) (bool, error) {
|
||||
return b.bucket.Exists(ctx, filepath.ToSlash(key))
|
||||
}
|
||||
|
||||
// listBucket returns a list of all files in the bucket within a given directory. go-cloud sorts the results by key
|
||||
func listBucket(bucket Bucket, dir string) ([]*blob.ListObject, error) {
|
||||
bucketIter := bucket.List(&blob.ListOptions{
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gocloud.dev/gcerrors"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/apitype"
|
||||
"github.com/pulumi/pulumi/pkg/backend"
|
||||
|
@ -267,7 +268,7 @@ func (b *localBackend) getHistory(name tokens.QName) ([]backend.UpdateInfo, erro
|
|||
allFiles, err := listBucket(b.bucket, dir)
|
||||
if err != nil {
|
||||
// History doesn't exist until a stack has been updated.
|
||||
if os.IsNotExist(err) {
|
||||
if gcerrors.Code(errors.Cause(err)) == gcerrors.NotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
|
@ -302,6 +303,42 @@ func (b *localBackend) getHistory(name tokens.QName) ([]backend.UpdateInfo, erro
|
|||
return updates, nil
|
||||
}
|
||||
|
||||
func (b *localBackend) renameHistory(oldName tokens.QName, newName tokens.QName) error {
|
||||
contract.Require(oldName != "", "oldName")
|
||||
contract.Require(newName != "", "newName")
|
||||
|
||||
oldHistory := b.historyDirectory(oldName)
|
||||
newHistory := b.historyDirectory(newName)
|
||||
|
||||
allFiles, err := listBucket(b.bucket, oldHistory)
|
||||
if err != nil {
|
||||
// if there's nothing there, we don't really need to do a rename.
|
||||
if gcerrors.Code(errors.Cause(err)) == gcerrors.NotFound {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range allFiles {
|
||||
fileName := objectName(file)
|
||||
oldBlob := path.Join(oldHistory, fileName)
|
||||
|
||||
// The filename format is <stack-name>-<timestamp>.[checkpoint|history].json, we need to change
|
||||
// the stack name part but retain the other parts.
|
||||
newFileName := string(newName) + fileName[strings.LastIndex(fileName, "-"):]
|
||||
newBlob := path.Join(newHistory, newFileName)
|
||||
|
||||
if err := b.bucket.Copy(context.TODO(), newBlob, oldBlob, nil); err != nil {
|
||||
return errors.Wrap(err, "copying history file")
|
||||
}
|
||||
if err := b.bucket.Delete(context.TODO(), oldBlob); err != nil {
|
||||
return errors.Wrap(err, "deleting existing history file")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addToHistory saves the UpdateInfo and makes a copy of the current Checkpoint file.
|
||||
func (b *localBackend) addToHistory(name tokens.QName, update backend.UpdateInfo) error {
|
||||
contract.Require(name != "", "name")
|
||||
|
|
Loading…
Reference in a new issue