/
backend.go
353 lines (309 loc) 路 15.1 KB
/
backend.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package backend
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/pulumi/pulumi/pkg/v3/backend/display"
"github.com/pulumi/pulumi/pkg/v3/engine"
"github.com/pulumi/pulumi/pkg/v3/operations"
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
"github.com/pulumi/pulumi/pkg/v3/resource/stack"
"github.com/pulumi/pulumi/pkg/v3/secrets"
"github.com/pulumi/pulumi/pkg/v3/util/cancel"
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
sdkDisplay "github.com/pulumi/pulumi/sdk/v3/go/common/display"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/result"
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
)
var (
// ErrNoPreviousDeployment is returned when there isn't a previous deployment.
ErrNoPreviousDeployment = errors.New("no previous deployment")
)
// StackAlreadyExistsError is returned from CreateStack when the stack already exists in the backend.
type StackAlreadyExistsError struct {
StackName string
}
func (e StackAlreadyExistsError) Error() string {
return fmt.Sprintf("stack '%v' already exists", e.StackName)
}
// OverStackLimitError is returned from CreateStack when the organization is billed per-stack and
// is over its stack limit.
type OverStackLimitError struct {
Message string
}
func (e OverStackLimitError) Error() string {
m := e.Message
m = strings.Replace(m, "Conflict: ", "over stack limit: ", -1)
return m
}
// StackReference is an opaque type that refers to a stack managed by a backend. The CLI uses the ParseStackReference
// method to turn a string like "my-great-stack" or "pulumi/my-great-stack" into a stack reference that can be used to
// interact with the stack via the backend. Stack references are specific to a given backend and different back ends
// may interpret the string passed to ParseStackReference differently.
type StackReference interface {
// fmt.Stringer's String() method returns a string of the stack identity, suitable for display in the CLI
fmt.Stringer
// Name is the name that will be passed to the Pulumi engine when preforming operations on this stack. This
// name may not uniquely identify the stack (e.g. the cloud backend embeds owner information in the StackReference
// but that information is not part of the StackName() we pass to the engine.
Name() tokens.Name
// Fully qualified name of the stack.
FullyQualifiedName() tokens.QName
}
// PolicyPackReference is an opaque type that refers to a PolicyPack managed by a backend. The CLI
// uses the ParsePolicyPackReference method to turn a string like "myOrg/mySecurityRules" into a
// PolicyPackReference that can be used to interact with the PolicyPack via the backend.
// PolicyPackReferences are specific to a given backend and different back ends may interpret the
// string passed to ParsePolicyPackReference differently.
type PolicyPackReference interface {
// fmt.Stringer's String() method returns a string of the stack identity, suitable for display in the CLI
fmt.Stringer
// OrgName is the name of the organization that is managing the PolicyPack.
OrgName() string
// Name is the name of the PolicyPack being referenced.
Name() tokens.QName
}
// StackSummary provides a basic description of a stack, without the ability to inspect its resources or make changes.
type StackSummary interface {
Name() StackReference
// LastUpdate returns when the stack was last updated, as applicable.
LastUpdate() *time.Time
// ResourceCount returns the stack's resource count, as applicable.
ResourceCount() *int
}
// ListStacksFilter describes optional filters when listing stacks.
type ListStacksFilter struct {
Organization *string
Project *string
TagName *string
TagValue *string
}
// ContinuationToken is an opaque string used for paginated backend requests. If non-nil, means
// there are more results to be returned and the continuation token should be passed into a
// subsequent call to the backend method. A nil continuation token means all results have been
// returned.
type ContinuationToken *string
// Backend is the contract between the Pulumi engine and pluggable backend implementations of the Pulumi Cloud Service.
type Backend interface {
// Name returns a friendly name for this backend.
Name() string
// URL returns a URL at which information about this backend may be seen.
URL() string
// GetPolicyPack returns a PolicyPack object tied to this backend, or nil if it cannot be found.
GetPolicyPack(ctx context.Context, policyPack string, d diag.Sink) (PolicyPack, error)
// ListPolicyGroups returns all Policy Groups for an organization in this backend or an error if it cannot be found.
ListPolicyGroups(ctx context.Context, orgName string, inContToken ContinuationToken) (
apitype.ListPolicyGroupsResponse, ContinuationToken, error)
// ListPolicyPacks returns all Policy Packs for an organization in this backend, or an error if it cannot be found.
ListPolicyPacks(ctx context.Context, orgName string, inContToken ContinuationToken) (
apitype.ListPolicyPacksResponse, ContinuationToken, error)
// SupportsTags tells whether a stack can have associated tags stored with it in this backend.
SupportsTags() bool
// SupportsOrganizations tells whether a user can belong to multiple organizations in this backend.
SupportsOrganizations() bool
// ParseStackReference takes a string representation and parses it to a reference which may be used for other
// methods in this backend.
ParseStackReference(s string) (StackReference, error)
// ValidateStackName verifies that the string is a legal identifier for a (potentially qualified) stack.
// Will check for any backend-specific naming restrictions.
ValidateStackName(s string) error
// DoesProjectExist returns true if a project with the given name exists in this backend, or false otherwise.
DoesProjectExist(ctx context.Context, projectName string) (bool, error)
// GetStack returns a stack object tied to this backend with the given name, or nil if it cannot be found.
GetStack(ctx context.Context, stackRef StackReference) (Stack, error)
// CreateStack creates a new stack with the given name and options that are specific to the backend provider.
CreateStack(ctx context.Context, stackRef StackReference, opts interface{}) (Stack, error)
// RemoveStack removes a stack with the given name. If force is true, the stack will be removed even if it
// still contains resources. Otherwise, if the stack contains resources, a non-nil error is returned, and the
// first boolean return value will be set to true.
RemoveStack(ctx context.Context, stack Stack, force bool) (bool, error)
// ListStacks returns a list of stack summaries for all known stacks in the target backend.
ListStacks(ctx context.Context, filter ListStacksFilter, inContToken ContinuationToken) (
[]StackSummary, ContinuationToken, error)
// RenameStack renames the given stack to a new name, and then returns an updated stack reference that
// can be used to refer to the newly renamed stack.
RenameStack(ctx context.Context, stack Stack, newName tokens.QName) (StackReference, error)
// Preview shows what would be updated given the current workspace's contents.
Preview(ctx context.Context, stack Stack, op UpdateOperation) (*deploy.Plan, sdkDisplay.ResourceChanges, result.Result)
// Update updates the target stack with the current workspace's contents (config and code).
Update(ctx context.Context, stack Stack, op UpdateOperation) (sdkDisplay.ResourceChanges, result.Result)
// Import imports resources into a stack.
Import(ctx context.Context, stack Stack, op UpdateOperation,
imports []deploy.Import) (sdkDisplay.ResourceChanges, result.Result)
// Refresh refreshes the stack's state from the cloud provider.
Refresh(ctx context.Context, stack Stack, op UpdateOperation) (sdkDisplay.ResourceChanges, result.Result)
// Destroy destroys all of this stack's resources.
Destroy(ctx context.Context, stack Stack, op UpdateOperation) (sdkDisplay.ResourceChanges, result.Result)
// Watch watches the project's working directory for changes and automatically updates the active stack.
Watch(ctx context.Context, stack Stack, op UpdateOperation, paths []string) result.Result
// Query against the resource outputs in a stack's state checkpoint.
Query(ctx context.Context, op QueryOperation) result.Result
// GetHistory returns all updates for the stack. The returned UpdateInfo slice will be in
// descending order (newest first).
GetHistory(ctx context.Context, stackRef StackReference, pageSize int, page int) ([]UpdateInfo, error)
// GetLogs fetches a list of log entries for the given stack, with optional filtering/querying.
GetLogs(ctx context.Context, stack Stack, cfg StackConfiguration,
query operations.LogQuery) ([]operations.LogEntry, error)
// Get the configuration from the most recent deployment of the stack.
GetLatestConfiguration(ctx context.Context, stack Stack) (config.Map, error)
// UpdateStackTags updates the stacks's tags, replacing all existing tags.
UpdateStackTags(ctx context.Context, stack Stack, tags map[apitype.StackTagName]string) error
// ExportDeployment exports the deployment for the given stack as an opaque JSON message.
ExportDeployment(ctx context.Context, stack Stack) (*apitype.UntypedDeployment, error)
// ImportDeployment imports the given deployment into the indicated stack.
ImportDeployment(ctx context.Context, stack Stack, deployment *apitype.UntypedDeployment) error
// Logout logs you out of the backend and removes any stored credentials.
Logout() error
// LogoutAll logs you out of all the backend and removes any stored credentials.
LogoutAll() error
// Returns the identity of the current user and any organizations they are in for the backend.
CurrentUser() (string, []string, error)
// Cancel the current update for the given stack.
CancelCurrentUpdate(ctx context.Context, stackRef StackReference) error
}
// SpecificDeploymentExporter is an interface defining an additional capability of a Backend, specifically the
// ability to export a specific versions of a stack's deployment. This isn't a requirement for all backends and
// should be checked for dynamically.
type SpecificDeploymentExporter interface {
// ExportDeploymentForVersion exports a specific deployment from the history of a stack. The meaning of
// version is backend-specific. For the Pulumi Console, it is the numeric version. (The first update
// being version "1", the second "2", and so on.) Though this might change in the future to use some
// other type of identifier or commitish .
ExportDeploymentForVersion(ctx context.Context, stack Stack, version string) (*apitype.UntypedDeployment, error)
}
// UpdateOperation is a complete stack update operation (preview, update, import, refresh, or destroy).
type UpdateOperation struct {
Proj *workspace.Project
Root string
Imports []deploy.Import
M *UpdateMetadata
Opts UpdateOptions
SecretsManager secrets.Manager
StackConfiguration StackConfiguration
Scopes CancellationScopeSource
}
// QueryOperation configures a query operation.
type QueryOperation struct {
Proj *workspace.Project
Root string
Opts UpdateOptions
SecretsManager secrets.Manager
StackConfiguration StackConfiguration
Scopes CancellationScopeSource
}
// StackConfiguration holds the configuration for a stack and it's associated decrypter.
type StackConfiguration struct {
Config config.Map
Decrypter config.Decrypter
}
// UpdateOptions is the full set of update options, including backend and engine options.
type UpdateOptions struct {
// Engine contains all of the engine-specific options.
Engine engine.UpdateOptions
// Display contains all of the backend display options.
Display display.Options
// AutoApprove, when true, will automatically approve previews.
AutoApprove bool
// SkipPreview, when true, causes the preview step to be skipped.
SkipPreview bool
}
// QueryOptions configures a query to operate against a backend and the engine.
type QueryOptions struct {
// Engine contains all of the engine-specific options.
Engine engine.UpdateOptions
// Display contains all of the backend display options.
Display display.Options
}
// CancellationScope provides a scoped source of cancellation and termination requests.
type CancellationScope interface {
// Context returns the cancellation context used to observe cancellation and termination requests for this scope.
Context() *cancel.Context
// Close closes the cancellation scope.
Close()
}
// CancellationScopeSource provides a source for cancellation scopes.
type CancellationScopeSource interface {
// NewScope creates a new cancellation scope.
NewScope(events chan<- engine.Event, isPreview bool) CancellationScope
}
// NewBackendClient returns a deploy.BackendClient that wraps the given Backend.
func NewBackendClient(backend Backend) deploy.BackendClient {
return &backendClient{backend: backend}
}
type backendClient struct {
backend Backend
}
// GetStackOutputs returns the outputs of the stack with the given name.
func (c *backendClient) GetStackOutputs(ctx context.Context, name string) (resource.PropertyMap, error) {
ref, err := c.backend.ParseStackReference(name)
if err != nil {
return nil, err
}
s, err := c.backend.GetStack(ctx, ref)
if err != nil {
return nil, err
}
if s == nil {
return nil, fmt.Errorf("unknown stack %q", name)
}
snap, err := s.Snapshot(ctx)
if err != nil {
return nil, err
}
res, err := stack.GetRootStackResource(snap)
if err != nil {
return nil, fmt.Errorf("getting root stack resources: %w", err)
}
if res == nil {
return resource.PropertyMap{}, nil
}
return res.Outputs, nil
}
func (c *backendClient) GetStackResourceOutputs(
ctx context.Context, name string) (resource.PropertyMap, error) {
ref, err := c.backend.ParseStackReference(name)
if err != nil {
return nil, err
}
s, err := c.backend.GetStack(ctx, ref)
if err != nil {
return nil, err
}
if s == nil {
return nil, fmt.Errorf("unknown stack %q", name)
}
snap, err := s.Snapshot(ctx)
if err != nil {
return nil, err
}
pm := resource.PropertyMap{}
for _, r := range snap.Resources {
if r.Delete {
continue
}
resc := resource.PropertyMap{
resource.PropertyKey("type"): resource.NewStringProperty(string(r.Type)),
resource.PropertyKey("outputs"): resource.NewObjectProperty(r.Outputs)}
pm[resource.PropertyKey(r.URN)] = resource.NewObjectProperty(resc)
}
return pm, nil
}