diff --git a/cmd/crossplane/simulate/help/simulate.md b/cmd/crossplane/simulate/help/simulate.md index b980ff43..86b0fc30 100644 --- a/cmd/crossplane/simulate/help/simulate.md +++ b/cmd/crossplane/simulate/help/simulate.md @@ -16,6 +16,14 @@ claims, use `crossplane xr simulate`; to simulate Compositions, use `crossplane composition simulate`; to simulate a project, use `crossplane project simulate`. +The command connects to the cluster in your current Kubernetes context, or the +one `--context` names. Use `--as`, `--as-group`, and `--as-uid` to simulate as +another user. + +`-f` (`--filename`) names a file, or a directory of YAML files, of managed +resources to simulate, and is required. Give a comma-separated list for more; +`-f -` reads standard input. + Which providers run as diff servers comes from the current kubeconfig context by default: the CLI follows each resource's kind to the provider that installed it on the cluster, so the preview matches the provider versions @@ -88,44 +96,3 @@ the result: replace a resource. - 80: the flags were invalid, so nothing was simulated. - 130: the simulation was interrupted. - -## Examples - -Preview what applying resources.yaml would do: - -```shell -crossplane resource simulate -f resources.yaml -``` - -Preview against specific provider versions instead of the installed ones, -from a checked-in file of Provider manifests: - -```shell -crossplane resource simulate -f resources.yaml --providers providers.yaml -``` - -Keep diff servers running so later runs start faster, via a provider manifest -annotated with `simulate.crossplane.io/runtime-docker-cleanup: Orphan`: - -```shell -crossplane resource simulate -f resources.yaml --providers providers.yaml -``` - -Iterate on a provider's diff server from source, with no package to build or -push: run `go run ./cmd/provider internal diff-server` in the provider -repository, then simulate with a Provider manifest annotated with -`simulate.crossplane.io/runtime: Development` and -`simulate.crossplane.io/runtime-development-api-groups: ec2.aws.upbound.io,ec2.aws.m.upbound.io`: - -```shell -crossplane resource simulate -f resources.yaml --providers providers.yaml -``` - -Emit machine-readable results for CI gating. Each plan carries the live -object as `before` (absent for a create) and the desired manifest as `after`, -both without status, server-managed metadata and ignored paths, and with -Secret values redacted: - -```shell -crossplane resource simulate -f resources.yaml -o json -``` diff --git a/cmd/crossplane/simulate/help/simulate_examples.md b/cmd/crossplane/simulate/help/simulate_examples.md new file mode 100644 index 00000000..0f09a437 --- /dev/null +++ b/cmd/crossplane/simulate/help/simulate_examples.md @@ -0,0 +1,40 @@ +## Examples + +Preview what applying resources.yaml would do: + +```shell +crossplane resource simulate -f resources.yaml +``` + +Preview against specific provider versions instead of the installed ones, +from a checked-in file of Provider manifests: + +```shell +crossplane resource simulate -f resources.yaml --providers providers.yaml +``` + +Keep diff servers running so later runs start faster, via a provider manifest +annotated with `simulate.crossplane.io/runtime-docker-cleanup: Orphan`: + +```shell +crossplane resource simulate -f resources.yaml --providers providers.yaml +``` + +Iterate on a provider's diff server from source, with no package to build or +push: run `go run ./cmd/provider internal diff-server` in the provider +repository, then simulate with a Provider manifest annotated with +`simulate.crossplane.io/runtime: Development` and +`simulate.crossplane.io/runtime-development-api-groups: ec2.aws.upbound.io,ec2.aws.m.upbound.io`: + +```shell +crossplane resource simulate -f resources.yaml --providers providers.yaml +``` + +Emit machine-readable results for CI gating. Each plan carries the live +object as `before` (absent for a create) and the desired manifest as `after`, +both without status and server-managed metadata, and with Secret values +redacted: + +```shell +crossplane resource simulate -f resources.yaml -o json +``` diff --git a/cmd/crossplane/simulate/simulate.go b/cmd/crossplane/simulate/simulate.go index 655ab87f..789d444a 100644 --- a/cmd/crossplane/simulate/simulate.go +++ b/cmd/crossplane/simulate/simulate.go @@ -51,25 +51,28 @@ import ( //go:embed help/simulate.md var helpDetail string +//go:embed help/simulate_examples.md +var helpExamples string + // Cmd previews what applying managed resources would do. type Cmd struct { - Filename string `help:"YAML files or directory with managed resources, or '-' for stdin (comma-separated)." predictor:"yaml_file_or_directory" required:"" short:"f"` + Filename string `help:"File or directory of managed resources to simulate. Takes a comma-separated list of paths. - reads standard input." predictor:"yaml_file_or_directory" required:"" short:"f"` - Providers string `help:"YAML file or directory with Provider manifests to run as diff servers, overriding discovery from the cluster. Supports per-provider runtime annotations." name:"providers" optional:"" predictor:"yaml_file_or_directory" type:"path"` - ImageConfigs string `help:"YAML file with ImageConfig manifests that rewrite provider image paths for local pulls. Defaults to the project's imageConfigs when a project file is present." name:"image-configs" optional:"" predictor:"yaml_file" type:"existingfile"` - ProjectFile string `help:"Path to a project definition file to read imageConfigs from. Defaults to ${project_file} when present." name:"project-file" optional:""` - SessionID string `help:"Session ID to apply as the simulate.crossplane.io/session label on diff server containers. Defaults to a random per-run ID. A supplied ID isn't leased, so its containers aren't reclaimed after a crash." placeholder:"ID"` - Context string `default:"" help:"Kubernetes context." name:"context" predictor:"context" short:"c"` - CacheDir string `env:"CROSSPLANE_XPKG_CACHE" help:"Cache directory for downloaded provider packages." name:"cache-dir" predictor:"directory"` - Timeout time.Duration `default:"2m" help:"Per-resource plan timeout."` - Output string `default:"plan" enum:"plan,json,yaml" help:"Output format. One of: plan, json, yaml." name:"output" short:"o"` + Providers string `help:"YAML file or directory of Provider manifests to run as diff servers, instead of discovering them from the cluster." name:"providers" optional:"" predictor:"yaml_file_or_directory" type:"path"` + ImageConfigs string `help:"YAML file of ImageConfig manifests that rewrite package paths for pulls. Defaults to the project's imageConfigs when a project file is present." name:"image-configs" optional:"" predictor:"yaml_file" type:"existingfile"` + ProjectFile string `help:"Project file to read imageConfigs from. Defaults to ${project_file} when present." name:"project-file" optional:""` + SessionID string `help:"Session ID to apply as the simulate.crossplane.io/session label on diff server containers. Defaults to a random per-process ID. A supplied ID isn't leased, so its containers aren't reclaimed after a crash." placeholder:"ID"` + Context string `default:"" help:"Kubernetes context to simulate against. Defaults to the current context." name:"context" predictor:"context" short:"c"` + CacheDir string `env:"CROSSPLANE_XPKG_CACHE" help:"Directory for cached xpkg package contents (providers)." name:"cache-dir" predictor:"directory"` + Timeout time.Duration `default:"2m" help:"Max time to plan one managed resource."` + Output string `default:"plan" enum:"plan,json,yaml" help:"Output format. One of: plan, json, yaml." name:"output" short:"o"` Impersonation kube.ImpersonationFlags `embed:""` } // Help returns the help message for the simulate command. func (c *Cmd) Help() string { - return helpDetail + return helpDetail + "\n" + helpExamples } // Run runs the simulate command. ctx is the signal-bound command context from diff --git a/cmd/crossplane/simulate/simulate_test.go b/cmd/crossplane/simulate/simulate_test.go index ceba17b5..a8dafbd6 100644 --- a/cmd/crossplane/simulate/simulate_test.go +++ b/cmd/crossplane/simulate/simulate_test.go @@ -17,6 +17,7 @@ limitations under the License. package simulate import ( + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -72,3 +73,33 @@ func TestExitStatus(t *testing.T) { }) } } + +func TestHelp(t *testing.T) { + type args struct { + want string + } + cases := map[string]struct { + reason string + args args + want bool + }{ + "Detail": { + reason: "Help should include the command description.", + args: args{want: "-f` (`--filename`)"}, + want: true, + }, + "Examples": { + reason: "Help should include the examples section.", + args: args{want: "## Examples"}, + want: true, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + got := strings.Contains((&Cmd{}).Help(), tc.args.want) + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("\n%s\nHelp() contains %q: -want, +got:\n%s", tc.reason, tc.args.want, diff) + } + }) + } +}