Skip to content

Commit 1795614

Browse files
authored
Add --show-kptfile option to porchctl rpkg get command (kptdev#513)
* Add --show-kptfile flag to porchctl get command to display Kptfile content Signed-off-by: Aravindhan Ayyanathan <aravindhan.a@est.tech> * Address copilot review comments Signed-off-by: Aravindhan Ayyanathan <aravindhan.a@est.tech> * Refactor namespace resolution and update documentation for --show-kptfile flag Signed-off-by: Aravindhan Ayyanathan <aravindhan.a@est.tech> --------- Signed-off-by: Aravindhan Ayyanathan <aravindhan.a@est.tech>
1 parent a8b123b commit 1795614

6 files changed

Lines changed: 470 additions & 9 deletions

File tree

docs/content/en/docs/4_tutorials_and_how-tos/working_with_package_revisions/inspecting-packages.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,47 @@ Use `jq` to extract specific fields: `porchctl rpkg get <name> -n default -o jso
140140

141141
---
142142

143+
### Viewing the Root Kptfile
144+
145+
Display the root Kptfile of a specific package revision without downloading the entire package
146+
147+
```bash
148+
porchctl rpkg get porch-test.my-app.v1 --show-kptfile --namespace default
149+
```
150+
151+
**What this does:**
152+
153+
- Fetches the PackageRevisionResources for the specified package revision
154+
- Extracts and displays only the root `Kptfile`
155+
- Useful for quickly inspecting package metadata, pipeline configuration, and status
156+
157+
**Example output:**
158+
159+
```yaml
160+
apiVersion: kpt.dev/v1
161+
kind: Kptfile
162+
metadata:
163+
name: my-app
164+
info:
165+
description: My application package
166+
pipeline:
167+
mutators:
168+
- image: ghcr.io/kptdev/krm-functions-catalog/set-namespace:v0.4.5
169+
configMap:
170+
namespace: production
171+
status:
172+
conditions:
173+
- type: Rendered
174+
status: "True"
175+
reason: RenderSuccess
176+
```
177+
178+
{{% alert title="Note" color="primary" %}}
179+
`--show-kptfile` requires exactly one package revision name and cannot be combined with `--name`, `--revision`, `--workspace`, or `--all-namespaces`.
180+
{{% /alert %}}
181+
182+
---
183+
143184
### Reading PackageRevision Resources
144185

145186
Read the actual contents of a PackageRevision:

docs/content/en/docs/7_cli_api/porchctl.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ porchctl rpkg get [PACKAGE] [flags]
406406
| `--name string` | Filter by package name (substring match) | |
407407
| `--revision int` | Filter by revision number | `-2` (all) |
408408
| `--workspace string` | Filter by workspace name | |
409+
| `--show-kptfile` | Display the root Kptfile of the specified package revision. Requires exactly one package revision name. Cannot be combined with `--name`, `--revision`, `--workspace`, or `-A`. | |
409410
| `-A, --all-namespaces` | List across all namespaces | |
410411
| `-o, --output string` | Output format | |
411412
| `--no-headers` | Don't print headers | |
@@ -425,6 +426,9 @@ porchctl rpkg get --namespace=example-namespace
425426

426427
# Get all with revision 0 (Draft/Proposed)
427428
porchctl rpkg get --revision=0
429+
430+
# Display the root Kptfile of a specific package revision
431+
porchctl rpkg get example-repo.example-package-name.example-workspace --show-kptfile --namespace=example-namespace
428432
```
429433

430434
---

pkg/cli/commands/rpkg/docs/docs.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ Flags:
146146
--revision
147147
Revision of the package to get. Any package whose revision
148148
matches this value will be included in the results.
149+
150+
--show-kptfile
151+
Display the root Kptfile of the specified package revision.
152+
Requires exactly one package revision name as an argument.
153+
Cannot be combined with --name, --revision, --workspace, or --all-namespaces.
149154
`
150155
var GetExamples = `
151156
# get a specific package revision using its kubernetes resource name in the 'example-namespace' namespace
@@ -159,6 +164,9 @@ var GetExamples = `
159164
160165
# get all package revisions with revision '0'
161166
$ porchctl rpkg get --revision=0
167+
168+
# display the root Kptfile of a specific package revision
169+
$ porchctl rpkg get example-repo.example-package-name.example-workspace --show-kptfile --namespace=example-namespace
162170
`
163171

164172
var InitShort = `Initializes a new package revision in a repository.`

pkg/cli/commands/rpkg/get/command.go

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020
"strconv"
2121
"strings"
2222

23+
kptfilev1 "github.com/kptdev/kpt/pkg/api/kptfile/v1"
2324
"github.com/kptdev/kpt/pkg/lib/errors"
2425
"github.com/kptdev/kpt/pkg/lib/util/cmdutil"
26+
porchapi "github.com/nephio-project/porch/api/porch/v1alpha1"
2527
cliutils "github.com/nephio-project/porch/internal/cliutils"
2628
"github.com/nephio-project/porch/pkg/cli/commands/rpkg/docs"
2729
"github.com/spf13/cobra"
@@ -35,6 +37,7 @@ import (
3537
"k8s.io/client-go/rest"
3638
"k8s.io/klog/v2"
3739
"k8s.io/kubectl/pkg/cmd/get"
40+
"sigs.k8s.io/controller-runtime/pkg/client"
3841
)
3942

4043
const (
@@ -66,6 +69,7 @@ func newRunner(ctx context.Context, rcg *genericclioptions.ConfigFlags) *runner
6669
cmd.Flags().Int64Var(&r.revision, "revision", -2, "Revision of the packages to get. Any package whose revision matches this value will be included in the results.")
6770
cmd.Flags().StringVar(&r.workspace, "workspace", "",
6871
"WorkspaceName of the packages to get. Any package whose workspaceName matches this value will be included in the results.")
72+
cmd.Flags().BoolVar(&r.showKptfile, "show-kptfile", false, "Display the root Kptfile of the specified package revision. Requires a single package revision name as an argument.")
6973

7074
r.getFlags.AddFlags(cmd)
7175
r.printFlags.AddFlags(cmd)
@@ -85,18 +89,22 @@ type runner struct {
8589
packageName string
8690
revision int64
8791
workspace string
92+
showKptfile bool
8893
printFlags *get.PrintFlags
8994

9095
requestTable bool
96+
97+
// client is used for --show-kptfile; set in preRunE or injected in tests
98+
client client.Client
9199
}
92100

93-
func (r *runner) preRunE(cmd *cobra.Command, _ []string) error {
94-
// Check for --namespace or -n flag specified without a value
95-
nsFlag := cmd.Flag("namespace")
96-
nFlag := cmd.Flag("n")
97-
if (nsFlag != nil && nsFlag.Changed && nsFlag.Value.String() == "") ||
98-
(nFlag != nil && nFlag.Changed && nFlag.Value.String() == "") {
99-
return fmt.Errorf("error: namespace flag specified without a value; please provide a value for --namespace/-n or omit the flag")
101+
func (r *runner) preRunE(cmd *cobra.Command, args []string) error {
102+
if err := r.validateNamespaceFlag(cmd); err != nil {
103+
return err
104+
}
105+
106+
if r.showKptfile {
107+
return r.preRunShowKptfile(args)
100108
}
101109

102110
// Print the namespace if we're spanning namespaces
@@ -113,9 +121,65 @@ func (r *runner) preRunE(cmd *cobra.Command, _ []string) error {
113121
return nil
114122
}
115123

124+
func (r *runner) validateNamespaceFlag(cmd *cobra.Command) error {
125+
nsFlag := cmd.Flag("namespace")
126+
nFlag := cmd.Flag("n")
127+
if (nsFlag != nil && nsFlag.Changed && nsFlag.Value.String() == "") ||
128+
(nFlag != nil && nFlag.Changed && nFlag.Value.String() == "") {
129+
return fmt.Errorf("namespace flag specified without a value; please provide a value for --namespace/-n or omit the flag")
130+
}
131+
return nil
132+
}
133+
134+
// resolveNamespace ensures a namespace is set on ConfigFlags, falling back to
135+
// the kubeconfig default context namespace when none was explicitly provided.
136+
func (r *runner) resolveNamespace() error {
137+
if r.getFlags.ConfigFlags.Namespace == nil || *r.getFlags.ConfigFlags.Namespace == "" {
138+
namespace, _, err := r.getFlags.ConfigFlags.ToRawKubeConfigLoader().Namespace()
139+
if err != nil {
140+
return fmt.Errorf("error resolving namespace from kubeconfig: %w", err)
141+
}
142+
if namespace == "" {
143+
return fmt.Errorf("namespace is not configured; please provide --namespace or set a default namespace in your kubeconfig")
144+
}
145+
r.getFlags.ConfigFlags.Namespace = &namespace
146+
}
147+
return nil
148+
}
149+
150+
func (r *runner) preRunShowKptfile(args []string) error {
151+
if len(args) != 1 {
152+
return fmt.Errorf("--show-kptfile requires exactly one package revision name as an argument")
153+
}
154+
if r.packageName != "" || r.revision != -2 || r.workspace != "" {
155+
return fmt.Errorf("--show-kptfile cannot be combined with --name, --revision, or --workspace")
156+
}
157+
if r.getFlags.AllNamespaces {
158+
return fmt.Errorf("--show-kptfile cannot be combined with --all-namespaces")
159+
}
160+
if err := r.resolveNamespace(); err != nil {
161+
return err
162+
}
163+
if r.client == nil {
164+
c, err := cliutils.CreateClientWithFlags(r.getFlags.ConfigFlags)
165+
if err != nil {
166+
return err
167+
}
168+
r.client = c
169+
}
170+
return nil
171+
}
172+
116173
func (r *runner) runE(cmd *cobra.Command, args []string) error {
117174
const op errors.Op = command + ".runE"
118175

176+
if r.showKptfile {
177+
if len(args) != 1 {
178+
return errors.E(op, fmt.Errorf("--show-kptfile requires exactly one package revision name as an argument"))
179+
}
180+
return r.showKptfileContent(cmd, args[0])
181+
}
182+
119183
var objs []runtime.Object
120184
b, err := r.getFlags.ResourceBuilder()
121185
if err != nil {
@@ -322,3 +386,33 @@ func (r *runner) filterTableRows(table *metav1.Table) error {
322386
table.Rows = filtered
323387
return nil
324388
}
389+
390+
func (r *runner) showKptfileContent(cmd *cobra.Command, name string) error {
391+
const op errors.Op = command + ".showKptfileContent"
392+
393+
if err := r.resolveNamespace(); err != nil {
394+
return errors.E(op, err)
395+
}
396+
397+
if r.client == nil {
398+
return errors.E(op, fmt.Errorf("client is not initialized; ensure preRunE has been executed"))
399+
}
400+
401+
var resources porchapi.PackageRevisionResources
402+
if err := r.client.Get(r.ctx, client.ObjectKey{
403+
Namespace: *r.getFlags.ConfigFlags.Namespace,
404+
Name: name,
405+
}, &resources); err != nil {
406+
return errors.E(op, err)
407+
}
408+
409+
kptfile, ok := resources.Spec.Resources[kptfilev1.KptFileName]
410+
if !ok {
411+
return errors.E(op, fmt.Errorf("package revision %q does not contain a root Kptfile", name))
412+
}
413+
414+
if _, err := fmt.Fprint(cmd.OutOrStdout(), kptfile); err != nil {
415+
return errors.E(op, err)
416+
}
417+
return nil
418+
}

0 commit comments

Comments
 (0)