Add tokens.StackName (#14487)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This adds a new type `tokens.StackName` which is a relatively strongly
typed container for a stack name. The only weakly typed aspect of it is
Go will always allow the "zero" value to be created for a struct, which
for a stack name is the empty string which is invalid. To prevent
introducing unexpected empty strings when working with stack names the
`String()` method will panic for zero initialized stack names.
Apart from the zero value, all other instances of `StackName` are via
`ParseStackName` which returns a descriptive error if the string is not
valid.
This PR only updates "pkg/" to use this type. There are a number of
places in "sdk/" which could do with this type as well, but there's no
harm in doing a staggered roll out, and some parts of "sdk/" are user
facing and will probably have to stay on the current `tokens.Name` and
`tokens.QName` types.
There are two places in the system where we panic on invalid stack
names, both in the http backend. This _should_ be fine as we've had long
standing validation that stacks created in the service are valid stack
names.
Just in case people have managed to introduce invalid stack names, there
is the `PULUMI_DISABLE_VALIDATION` environment variable which will turn
off the validation _and_ panicing for stack names. Users can use that to
temporarily disable the validation and continue working, but it should
only be seen as a temporary measure. If they have invalid names they
should rename them, or if they think they should be valid raise an issue
with us to change the validation code.
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-15 07:44:54 +00:00
|
|
|
// Copyright 2016-2023, Pulumi Corporation.
|
2018-05-22 19:43:36 +00:00
|
|
|
//
|
|
|
|
// 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.
|
Improve the overall cloud CLI experience
This improves the overall cloud CLI experience workflow.
Now whether a stack is local or cloud is inherent to the stack
itself. If you interact with a cloud stack, we transparently talk
to the cloud; if you interact with a local stack, we just do the
right thing, and perform all operations locally. Aside from sometimes
seeing a cloud emoji pop-up ☁️, the experience is quite similar.
For example, to initialize a new cloud stack, simply:
$ pulumi login
Logging into Pulumi Cloud: https://pulumi.com/
Enter Pulumi access token: <enter your token>
$ pulumi stack init my-cloud-stack
Note that you may log into a specific cloud if you'd like. For
now, this is just for our own testing purposes, but someday when we
support custom clouds (e.g., Enterprise), you can just say:
$ pulumi login --cloud-url https://corp.acme.my-ppc.net:9873
The cloud is now the default. If you instead prefer a "fire and
forget" style of stack, you can skip the login and pass `--local`:
$ pulumi stack init my-faf-stack --local
If you are logged in and run `pulumi`, we tell you as much:
$ pulumi
Usage:
pulumi [command]
// as before...
Currently logged into the Pulumi Cloud ☁️
https://pulumi.com/
And if you list your stacks, we tell you which one is local or not:
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT CLOUD URL
my-cloud-stack 2017-12-01 ... 3 https://pulumi.com/
my-faf-stack n/a 0 n/a
And `pulumi stack` by itself prints information like your cloud org,
PPC name, and so on, in addition to the usuals.
I shall write up more details and make sure to document these changes.
This change also fairly significantly refactors the layout of cloud
versus local logic, so that the cmd/ package is resonsible for CLI
things, and the new pkg/backend/ package is responsible for the
backends. The following is the overall resulting package architecture:
* The backend.Backend interface can be implemented to substitute
a new backend. This has operations to get and list stacks,
perform updates, and so on.
* The backend.Stack struct is a wrapper around a stack that has
or is being manipulated by a Backend. It resembles our existing
Stack notions in the engine, but carries additional metadata
about its source. Notably, it offers functions that allow
operations like updating and deleting on the Backend from which
it came.
* There is very little else in the pkg/backend/ package.
* A new package, pkg/backend/local/, encapsulates all local state
management for "fire and forget" scenarios. It simply implements
the above logic and contains anything specific to the local
experience.
* A peer package, pkg/backend/cloud/, encapsulates all logic
required for the cloud experience. This includes its subpackage
apitype/ which contains JSON schema descriptions required for
REST calls against the cloud backend. It also contains handy
functions to list which clouds we have authenticated with.
* A subpackage here, pkg/backend/state/, is not a provider at all.
Instead, it contains all of the state management functions that
are currently shared between local and cloud backends. This
includes configuration logic -- including encryption -- as well
as logic pertaining to which stacks are known to the workspace.
This addresses pulumi/pulumi#629 and pulumi/pulumi#494.
2017-12-02 15:29:46 +00:00
|
|
|
|
2018-09-04 22:40:15 +00:00
|
|
|
package display
|
Improve the overall cloud CLI experience
This improves the overall cloud CLI experience workflow.
Now whether a stack is local or cloud is inherent to the stack
itself. If you interact with a cloud stack, we transparently talk
to the cloud; if you interact with a local stack, we just do the
right thing, and perform all operations locally. Aside from sometimes
seeing a cloud emoji pop-up ☁️, the experience is quite similar.
For example, to initialize a new cloud stack, simply:
$ pulumi login
Logging into Pulumi Cloud: https://pulumi.com/
Enter Pulumi access token: <enter your token>
$ pulumi stack init my-cloud-stack
Note that you may log into a specific cloud if you'd like. For
now, this is just for our own testing purposes, but someday when we
support custom clouds (e.g., Enterprise), you can just say:
$ pulumi login --cloud-url https://corp.acme.my-ppc.net:9873
The cloud is now the default. If you instead prefer a "fire and
forget" style of stack, you can skip the login and pass `--local`:
$ pulumi stack init my-faf-stack --local
If you are logged in and run `pulumi`, we tell you as much:
$ pulumi
Usage:
pulumi [command]
// as before...
Currently logged into the Pulumi Cloud ☁️
https://pulumi.com/
And if you list your stacks, we tell you which one is local or not:
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT CLOUD URL
my-cloud-stack 2017-12-01 ... 3 https://pulumi.com/
my-faf-stack n/a 0 n/a
And `pulumi stack` by itself prints information like your cloud org,
PPC name, and so on, in addition to the usuals.
I shall write up more details and make sure to document these changes.
This change also fairly significantly refactors the layout of cloud
versus local logic, so that the cmd/ package is resonsible for CLI
things, and the new pkg/backend/ package is responsible for the
backends. The following is the overall resulting package architecture:
* The backend.Backend interface can be implemented to substitute
a new backend. This has operations to get and list stacks,
perform updates, and so on.
* The backend.Stack struct is a wrapper around a stack that has
or is being manipulated by a Backend. It resembles our existing
Stack notions in the engine, but carries additional metadata
about its source. Notably, it offers functions that allow
operations like updating and deleting on the Backend from which
it came.
* There is very little else in the pkg/backend/ package.
* A new package, pkg/backend/local/, encapsulates all local state
management for "fire and forget" scenarios. It simply implements
the above logic and contains anything specific to the local
experience.
* A peer package, pkg/backend/cloud/, encapsulates all logic
required for the cloud experience. This includes its subpackage
apitype/ which contains JSON schema descriptions required for
REST calls against the cloud backend. It also contains handy
functions to list which clouds we have authenticated with.
* A subpackage here, pkg/backend/state/, is not a provider at all.
Instead, it contains all of the state management functions that
are currently shared between local and cloud backends. This
includes configuration logic -- including encryption -- as well
as logic pertaining to which stacks are known to the workspace.
This addresses pulumi/pulumi#629 and pulumi/pulumi#494.
2017-12-02 15:29:46 +00:00
|
|
|
|
|
|
|
import (
|
2019-09-07 00:07:54 +00:00
|
|
|
"encoding/json"
|
Improve the overall cloud CLI experience
This improves the overall cloud CLI experience workflow.
Now whether a stack is local or cloud is inherent to the stack
itself. If you interact with a cloud stack, we transparently talk
to the cloud; if you interact with a local stack, we just do the
right thing, and perform all operations locally. Aside from sometimes
seeing a cloud emoji pop-up ☁️, the experience is quite similar.
For example, to initialize a new cloud stack, simply:
$ pulumi login
Logging into Pulumi Cloud: https://pulumi.com/
Enter Pulumi access token: <enter your token>
$ pulumi stack init my-cloud-stack
Note that you may log into a specific cloud if you'd like. For
now, this is just for our own testing purposes, but someday when we
support custom clouds (e.g., Enterprise), you can just say:
$ pulumi login --cloud-url https://corp.acme.my-ppc.net:9873
The cloud is now the default. If you instead prefer a "fire and
forget" style of stack, you can skip the login and pass `--local`:
$ pulumi stack init my-faf-stack --local
If you are logged in and run `pulumi`, we tell you as much:
$ pulumi
Usage:
pulumi [command]
// as before...
Currently logged into the Pulumi Cloud ☁️
https://pulumi.com/
And if you list your stacks, we tell you which one is local or not:
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT CLOUD URL
my-cloud-stack 2017-12-01 ... 3 https://pulumi.com/
my-faf-stack n/a 0 n/a
And `pulumi stack` by itself prints information like your cloud org,
PPC name, and so on, in addition to the usuals.
I shall write up more details and make sure to document these changes.
This change also fairly significantly refactors the layout of cloud
versus local logic, so that the cmd/ package is resonsible for CLI
things, and the new pkg/backend/ package is responsible for the
backends. The following is the overall resulting package architecture:
* The backend.Backend interface can be implemented to substitute
a new backend. This has operations to get and list stacks,
perform updates, and so on.
* The backend.Stack struct is a wrapper around a stack that has
or is being manipulated by a Backend. It resembles our existing
Stack notions in the engine, but carries additional metadata
about its source. Notably, it offers functions that allow
operations like updating and deleting on the Backend from which
it came.
* There is very little else in the pkg/backend/ package.
* A new package, pkg/backend/local/, encapsulates all local state
management for "fire and forget" scenarios. It simply implements
the above logic and contains anything specific to the local
experience.
* A peer package, pkg/backend/cloud/, encapsulates all logic
required for the cloud experience. This includes its subpackage
apitype/ which contains JSON schema descriptions required for
REST calls against the cloud backend. It also contains handy
functions to list which clouds we have authenticated with.
* A subpackage here, pkg/backend/state/, is not a provider at all.
Instead, it contains all of the state management functions that
are currently shared between local and cloud backends. This
includes configuration logic -- including encryption -- as well
as logic pertaining to which stacks are known to the workspace.
This addresses pulumi/pulumi#629 and pulumi/pulumi#494.
2017-12-02 15:29:46 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2019-09-07 00:07:54 +00:00
|
|
|
"os"
|
2019-12-16 17:57:54 +00:00
|
|
|
"time"
|
2018-09-24 20:49:14 +00:00
|
|
|
|
2023-03-06 17:06:48 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/backend/display/internal/terminal"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/engine"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
|
2023-11-20 21:55:59 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/channel"
|
2021-09-22 00:22:39 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
|
2021-10-26 23:21:27 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/logging"
|
Improve the overall cloud CLI experience
This improves the overall cloud CLI experience workflow.
Now whether a stack is local or cloud is inherent to the stack
itself. If you interact with a cloud stack, we transparently talk
to the cloud; if you interact with a local stack, we just do the
right thing, and perform all operations locally. Aside from sometimes
seeing a cloud emoji pop-up ☁️, the experience is quite similar.
For example, to initialize a new cloud stack, simply:
$ pulumi login
Logging into Pulumi Cloud: https://pulumi.com/
Enter Pulumi access token: <enter your token>
$ pulumi stack init my-cloud-stack
Note that you may log into a specific cloud if you'd like. For
now, this is just for our own testing purposes, but someday when we
support custom clouds (e.g., Enterprise), you can just say:
$ pulumi login --cloud-url https://corp.acme.my-ppc.net:9873
The cloud is now the default. If you instead prefer a "fire and
forget" style of stack, you can skip the login and pass `--local`:
$ pulumi stack init my-faf-stack --local
If you are logged in and run `pulumi`, we tell you as much:
$ pulumi
Usage:
pulumi [command]
// as before...
Currently logged into the Pulumi Cloud ☁️
https://pulumi.com/
And if you list your stacks, we tell you which one is local or not:
$ pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT CLOUD URL
my-cloud-stack 2017-12-01 ... 3 https://pulumi.com/
my-faf-stack n/a 0 n/a
And `pulumi stack` by itself prints information like your cloud org,
PPC name, and so on, in addition to the usuals.
I shall write up more details and make sure to document these changes.
This change also fairly significantly refactors the layout of cloud
versus local logic, so that the cmd/ package is resonsible for CLI
things, and the new pkg/backend/ package is responsible for the
backends. The following is the overall resulting package architecture:
* The backend.Backend interface can be implemented to substitute
a new backend. This has operations to get and list stacks,
perform updates, and so on.
* The backend.Stack struct is a wrapper around a stack that has
or is being manipulated by a Backend. It resembles our existing
Stack notions in the engine, but carries additional metadata
about its source. Notably, it offers functions that allow
operations like updating and deleting on the Backend from which
it came.
* There is very little else in the pkg/backend/ package.
* A new package, pkg/backend/local/, encapsulates all local state
management for "fire and forget" scenarios. It simply implements
the above logic and contains anything specific to the local
experience.
* A peer package, pkg/backend/cloud/, encapsulates all logic
required for the cloud experience. This includes its subpackage
apitype/ which contains JSON schema descriptions required for
REST calls against the cloud backend. It also contains handy
functions to list which clouds we have authenticated with.
* A subpackage here, pkg/backend/state/, is not a provider at all.
Instead, it contains all of the state management functions that
are currently shared between local and cloud backends. This
includes configuration logic -- including encryption -- as well
as logic pertaining to which stacks are known to the workspace.
This addresses pulumi/pulumi#629 and pulumi/pulumi#494.
2017-12-02 15:29:46 +00:00
|
|
|
)
|
|
|
|
|
2023-03-10 17:09:53 +00:00
|
|
|
// printPermalinkNonInteractive prints an update's permalink prefaced with `View Live: `.
|
|
|
|
// This message is printed in non-interactive scenarios.
|
|
|
|
// In order to maintain backwards compatibility with older versions of the Automation API,
|
|
|
|
// the message is not changed for non-interactive scenarios.
|
|
|
|
func printPermalinkNonInteractive(out io.Writer, opts Options, permalink string) {
|
2023-03-06 17:06:48 +00:00
|
|
|
printPermalink(out, opts, "View Live", permalink)
|
|
|
|
}
|
|
|
|
|
2023-03-10 17:09:53 +00:00
|
|
|
// printPermalinkInteractive prints an update's permalink prefaced with `View in Browser (Ctrl+O): `.
|
|
|
|
// This is printed in interactive scenarios that use the tree renderer.
|
|
|
|
func printPermalinkInteractive(term terminal.Terminal, opts Options, permalink string) {
|
|
|
|
printPermalink(term, opts, "View in Browser (Ctrl+O)", permalink)
|
2023-03-06 17:06:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func printPermalink(out io.Writer, opts Options, message, permalink string) {
|
|
|
|
if !opts.SuppressPermalink && permalink != "" {
|
|
|
|
// Print a URL at the beginning of the update pointing to the Pulumi Service.
|
|
|
|
headline := colors.SpecHeadline + message + ": " + colors.Underline + colors.BrightBlue + permalink +
|
|
|
|
colors.Reset + "\n\n"
|
|
|
|
fmt.Fprint(out, opts.Color.Colorize(headline))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:25:23 +00:00
|
|
|
// ShowEvents reads events from the `events` channel until it is closed, displaying each event as
|
2018-04-10 19:03:11 +00:00
|
|
|
// it comes in. Once all events have been read from the channel and displayed, it closes the `done`
|
|
|
|
// channel so the caller can await all the events being written.
|
2018-10-30 22:42:33 +00:00
|
|
|
func ShowEvents(
|
Add tokens.StackName (#14487)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This adds a new type `tokens.StackName` which is a relatively strongly
typed container for a stack name. The only weakly typed aspect of it is
Go will always allow the "zero" value to be created for a struct, which
for a stack name is the empty string which is invalid. To prevent
introducing unexpected empty strings when working with stack names the
`String()` method will panic for zero initialized stack names.
Apart from the zero value, all other instances of `StackName` are via
`ParseStackName` which returns a descriptive error if the string is not
valid.
This PR only updates "pkg/" to use this type. There are a number of
places in "sdk/" which could do with this type as well, but there's no
harm in doing a staggered roll out, and some parts of "sdk/" are user
facing and will probably have to stay on the current `tokens.Name` and
`tokens.QName` types.
There are two places in the system where we panic on invalid stack
names, both in the http backend. This _should_ be fine as we've had long
standing validation that stacks created in the service are valid stack
names.
Just in case people have managed to introduce invalid stack names, there
is the `PULUMI_DISABLE_VALIDATION` environment variable which will turn
off the validation _and_ panicing for stack names. Users can use that to
temporarily disable the validation and continue working, but it should
only be seen as a temporary measure. If they have invalid names they
should rename them, or if they think they should be valid raise an issue
with us to change the validation code.
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-15 07:44:54 +00:00
|
|
|
op string, action apitype.UpdateKind, stack tokens.StackName, proj tokens.PackageName,
|
2023-03-06 17:06:48 +00:00
|
|
|
permalink string, events <-chan engine.Event, done chan<- bool, opts Options, isPreview bool,
|
2023-03-03 16:36:39 +00:00
|
|
|
) {
|
2023-11-20 21:55:59 +00:00
|
|
|
// Need to filter the engine events here to exclude any internal events.
|
|
|
|
events = channel.FilterRead(events, func(e engine.Event) bool {
|
|
|
|
return !e.Internal()
|
|
|
|
})
|
|
|
|
|
2019-09-07 00:07:54 +00:00
|
|
|
if opts.EventLogPath != "" {
|
2021-09-22 00:22:39 +00:00
|
|
|
events, done = startEventLogger(events, done, opts)
|
2019-09-07 00:07:54 +00:00
|
|
|
}
|
|
|
|
|
2021-10-26 23:21:27 +00:00
|
|
|
streamPreview := cmdutil.IsTruthy(os.Getenv("PULUMI_ENABLE_STREAMING_JSON_PREVIEW"))
|
|
|
|
|
2019-04-20 20:34:58 +00:00
|
|
|
if opts.JSONDisplay {
|
2021-10-26 23:21:27 +00:00
|
|
|
if isPreview && !streamPreview {
|
|
|
|
ShowPreviewDigest(events, done, opts)
|
|
|
|
} else {
|
|
|
|
ShowJSONEvents(events, done, opts)
|
|
|
|
}
|
2019-04-30 17:31:53 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-03-06 17:06:48 +00:00
|
|
|
if opts.Type != DisplayProgress {
|
2023-03-10 17:09:53 +00:00
|
|
|
printPermalinkNonInteractive(os.Stdout, opts, permalink)
|
2023-03-06 17:06:48 +00:00
|
|
|
}
|
|
|
|
|
2019-04-30 17:31:53 +00:00
|
|
|
switch opts.Type {
|
|
|
|
case DisplayDiff:
|
2022-04-07 16:03:19 +00:00
|
|
|
ShowDiffEvents(op, events, done, opts)
|
2019-04-30 17:31:53 +00:00
|
|
|
case DisplayProgress:
|
2023-03-06 17:06:48 +00:00
|
|
|
ShowProgressEvents(op, action, stack, proj, permalink, events, done, opts, isPreview)
|
2019-04-30 17:31:53 +00:00
|
|
|
case DisplayQuery:
|
|
|
|
contract.Failf("DisplayQuery can only be used in query mode, which should be invoked " +
|
|
|
|
"directly instead of through ShowEvents")
|
2019-11-06 20:56:29 +00:00
|
|
|
case DisplayWatch:
|
2022-04-07 16:03:19 +00:00
|
|
|
ShowWatchEvents(op, events, done, opts)
|
2019-04-30 17:31:53 +00:00
|
|
|
default:
|
|
|
|
contract.Failf("Unknown display type %d", opts.Type)
|
2018-04-10 19:03:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-26 23:21:27 +00:00
|
|
|
func logJSONEvent(encoder *json.Encoder, event engine.Event, opts Options, seq int) error {
|
2022-04-07 16:03:19 +00:00
|
|
|
apiEvent, err := ConvertEngineEvent(event, false /* showSecrets */)
|
2021-10-26 23:21:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
apiEvent.Sequence = seq
|
|
|
|
apiEvent.Timestamp = int(time.Now().Unix())
|
|
|
|
// If opts.Color == "never" (i.e. NO_COLOR is specified or --color=never), clean up the color directives
|
|
|
|
// from the emitted events.
|
|
|
|
if opts.Color == colors.Never {
|
|
|
|
switch {
|
|
|
|
case apiEvent.DiagnosticEvent != nil:
|
|
|
|
apiEvent.DiagnosticEvent.Message = colors.Never.Colorize(apiEvent.DiagnosticEvent.Message)
|
|
|
|
apiEvent.DiagnosticEvent.Prefix = colors.Never.Colorize(apiEvent.DiagnosticEvent.Prefix)
|
|
|
|
apiEvent.DiagnosticEvent.Color = string(colors.Never)
|
|
|
|
case apiEvent.StdoutEvent != nil:
|
|
|
|
apiEvent.StdoutEvent.Message = colors.Never.Colorize(apiEvent.StdoutEvent.Message)
|
|
|
|
apiEvent.StdoutEvent.Color = string(colors.Never)
|
|
|
|
case apiEvent.PolicyEvent != nil:
|
|
|
|
apiEvent.PolicyEvent.Message = colors.Never.Colorize(apiEvent.PolicyEvent.Message)
|
|
|
|
apiEvent.PolicyEvent.Color = string(colors.Never)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return encoder.Encode(apiEvent)
|
|
|
|
}
|
|
|
|
|
2021-09-22 00:22:39 +00:00
|
|
|
func startEventLogger(events <-chan engine.Event, done chan<- bool, opts Options) (<-chan engine.Event, chan<- bool) {
|
2019-09-07 00:07:54 +00:00
|
|
|
// Before moving further, attempt to open the log file.
|
2023-02-15 09:35:17 +00:00
|
|
|
//
|
|
|
|
// Try setting O_APPEND to see if that helps with the malformed reads we've been seeing in automation api:
|
|
|
|
// https://github.com/pulumi/pulumi/issues/6768
|
2023-03-03 16:36:39 +00:00
|
|
|
logFile, err := os.OpenFile(opts.EventLogPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0o666)
|
2019-09-07 00:07:54 +00:00
|
|
|
if err != nil {
|
|
|
|
logging.V(7).Infof("could not create event log: %v", err)
|
|
|
|
return events, done
|
|
|
|
}
|
|
|
|
|
|
|
|
outEvents, outDone := make(chan engine.Event), make(chan bool)
|
|
|
|
go func() {
|
|
|
|
defer close(done)
|
|
|
|
defer func() {
|
|
|
|
contract.IgnoreError(logFile.Close())
|
|
|
|
}()
|
|
|
|
|
2019-12-16 17:57:54 +00:00
|
|
|
sequence := 0
|
2019-09-07 00:07:54 +00:00
|
|
|
encoder := json.NewEncoder(logFile)
|
2021-09-22 00:22:39 +00:00
|
|
|
encoder.SetEscapeHTML(false)
|
2019-09-07 00:07:54 +00:00
|
|
|
for e := range events {
|
2021-10-26 23:21:27 +00:00
|
|
|
if err = logJSONEvent(encoder, e, opts, sequence); err != nil {
|
2019-09-07 00:07:54 +00:00
|
|
|
logging.V(7).Infof("failed to log event: %v", err)
|
|
|
|
}
|
2021-10-26 23:21:27 +00:00
|
|
|
sequence++
|
2019-09-07 00:07:54 +00:00
|
|
|
|
|
|
|
outEvents <- e
|
|
|
|
|
|
|
|
if e.Type == engine.CancelEvent {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
<-outDone
|
|
|
|
}()
|
|
|
|
|
|
|
|
return outEvents, outDone
|
|
|
|
}
|
|
|
|
|
2023-03-03 16:36:39 +00:00
|
|
|
type nopSpinner struct{}
|
2018-04-24 21:23:08 +00:00
|
|
|
|
|
|
|
func (s *nopSpinner) Tick() {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *nopSpinner) Reset() {
|
|
|
|
}
|
|
|
|
|
2018-02-04 09:18:06 +00:00
|
|
|
// isRootStack returns true if the step pertains to the rootmost stack component.
|
2018-03-31 19:08:48 +00:00
|
|
|
func isRootStack(step engine.StepEventMetadata) bool {
|
2020-07-09 14:19:12 +00:00
|
|
|
return isRootURN(step.URN)
|
2018-04-10 19:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func isRootURN(urn resource.URN) bool {
|
2023-12-04 10:36:51 +00:00
|
|
|
return urn != "" && urn.QualifiedType() == resource.RootStackType
|
2018-02-04 09:18:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// shouldShow returns true if a step should show in the output.
|
2018-09-04 22:40:15 +00:00
|
|
|
func shouldShow(step engine.StepEventMetadata, opts Options) bool {
|
2018-02-04 09:18:06 +00:00
|
|
|
// For certain operations, whether they are tracked is controlled by flags (to cut down on superfluous output).
|
|
|
|
if step.Op == deploy.OpSame {
|
|
|
|
// If the op is the same, it is possible that the resource's metadata changed. In that case, still show it.
|
|
|
|
if step.Old.Protect != step.New.Protect {
|
|
|
|
return true
|
|
|
|
}
|
2018-04-14 05:26:01 +00:00
|
|
|
return opts.ShowSameResources
|
2018-02-04 09:18:06 +00:00
|
|
|
}
|
2018-08-02 04:48:14 +00:00
|
|
|
|
display: only hide replacement steps in diff (#16065)
When displaying diff events, we currently try to hide non-logical
replacement steps unless we specifically enable showing them. However we
currently do that for all non-logical operations, regardless whether
they are replacement steps or not.
In particular a RefreshStep is non-logical, but it's also not a
replacement step. We still want to show them during the diff because
their output can be important. Especially if the user just requested a
diff it doesn't make sense to hide the diff from them at the same time.
The intention here is to only hide replacement steps, so do that.
The full diff with the display tests is here:
https://gist.github.com/tgummerer/fcd012f13669a9cdc39530cde7770260 It's
unedited, so it includes some flakyness which isn't interesting.
I looked it over, and I think it looks like what we want, but I'm
curious to hear what others think. E.g.
https://gist.github.com/tgummerer/fcd012f13669a9cdc39530cde7770260#file-testdata-diff-L558
looks more correct now, as it shows the two delete operation that
actually happened, that it didn't show before, and it still shows the
same operation (Calling this one out in particular, since it took me a
bit to understand that we still have the same operation in the diff)
Fixes https://github.com/pulumi/pulumi/issues/7665
2024-04-26 14:54:21 +00:00
|
|
|
// For non-logical replacement operations, only show them during progress-style updates (since this is integrated
|
2019-04-23 22:55:18 +00:00
|
|
|
// into the resource status update), or if it is requested explicitly (for diffs and JSON outputs).
|
display: only hide replacement steps in diff (#16065)
When displaying diff events, we currently try to hide non-logical
replacement steps unless we specifically enable showing them. However we
currently do that for all non-logical operations, regardless whether
they are replacement steps or not.
In particular a RefreshStep is non-logical, but it's also not a
replacement step. We still want to show them during the diff because
their output can be important. Especially if the user just requested a
diff it doesn't make sense to hide the diff from them at the same time.
The intention here is to only hide replacement steps, so do that.
The full diff with the display tests is here:
https://gist.github.com/tgummerer/fcd012f13669a9cdc39530cde7770260 It's
unedited, so it includes some flakyness which isn't interesting.
I looked it over, and I think it looks like what we want, but I'm
curious to hear what others think. E.g.
https://gist.github.com/tgummerer/fcd012f13669a9cdc39530cde7770260#file-testdata-diff-L558
looks more correct now, as it shows the two delete operation that
actually happened, that it didn't show before, and it still shows the
same operation (Calling this one out in particular, since it took me a
bit to understand that we still have the same operation in the diff)
Fixes https://github.com/pulumi/pulumi/issues/7665
2024-04-26 14:54:21 +00:00
|
|
|
if !opts.ShowReplacementSteps {
|
|
|
|
if (opts.Type == DisplayDiff || opts.JSONDisplay) && !step.Logical && deploy.IsReplacementStep(step.Op) {
|
|
|
|
return false
|
|
|
|
}
|
2019-04-23 22:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, default to showing the operation.
|
2018-02-04 09:18:06 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2018-03-14 19:01:22 +00:00
|
|
|
func fprintfIgnoreError(w io.Writer, format string, a ...interface{}) {
|
|
|
|
_, err := fmt.Fprintf(w, format, a...)
|
|
|
|
contract.IgnoreError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fprintIgnoreError(w io.Writer, a ...interface{}) {
|
|
|
|
_, err := fmt.Fprint(w, a...)
|
|
|
|
contract.IgnoreError(err)
|
|
|
|
}
|