Skip to content

Commit c10249e

Browse files
authored
default revision for rpkg copy (#3185)
1 parent d76d6c3 commit c10249e

File tree

2 files changed

+98
-17
lines changed

2 files changed

+98
-17
lines changed

e2e/testdata/porch/rpkg-copy/config.yaml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,25 @@ commands:
1818
- --revision=v0
1919
- basens-edit
2020
stdout: git-f1fbc2b72ee70b2f34e64c2630031d0560e6cf48 created
21+
- args:
22+
- alpha
23+
- rpkg
24+
- propose
25+
- --namespace=rpkg-copy
26+
- git-f1fbc2b72ee70b2f34e64c2630031d0560e6cf48
27+
stderr: "git-f1fbc2b72ee70b2f34e64c2630031d0560e6cf48 proposed\n"
28+
- args:
29+
- alpha
30+
- rpkg
31+
- approve
32+
- --namespace=rpkg-copy
33+
- git-f1fbc2b72ee70b2f34e64c2630031d0560e6cf48
34+
stderr: "git-f1fbc2b72ee70b2f34e64c2630031d0560e6cf48 approved\n"
2135
- args:
2236
- alpha
2337
- rpkg
2438
- copy
2539
- --namespace=rpkg-copy
26-
- --revision=v1
2740
- git-f1fbc2b72ee70b2f34e64c2630031d0560e6cf48
2841
stdout: git-66579ccd78cf67f80a3cafaf63dc121db4d92b83 created
2942
- args:

internal/cmdrpkgcopy/command.go

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package cmdrpkgcopy
1717
import (
1818
"context"
1919
"fmt"
20+
"regexp"
21+
"strconv"
2022

2123
"github.com/GoogleContainerTools/kpt/internal/docs/generated/rpkgdocs"
2224
"github.com/GoogleContainerTools/kpt/internal/errors"
@@ -26,6 +28,7 @@ import (
2628
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2729
"k8s.io/apimachinery/pkg/runtime"
2830
"k8s.io/cli-runtime/pkg/genericclioptions"
31+
"k8s.io/client-go/rest"
2932
"sigs.k8s.io/controller-runtime/pkg/client"
3033
)
3134

@@ -42,7 +45,7 @@ func newRunner(ctx context.Context, rcg *genericclioptions.ConfigFlags) *runner
4245
ctx: ctx,
4346
cfg: rcg,
4447
}
45-
c := &cobra.Command{
48+
r.Command = &cobra.Command{
4649
Use: "copy SOURCE_PACKAGE NAME",
4750
Aliases: []string{"edit"},
4851
Short: rpkgdocs.CopyShort,
@@ -52,10 +55,7 @@ func newRunner(ctx context.Context, rcg *genericclioptions.ConfigFlags) *runner
5255
RunE: r.runE,
5356
Hidden: porch.HidePorchCommands,
5457
}
55-
r.Command = c
56-
// TODO (natasha41575): Make the default "latest+1"
57-
c.Flags().StringVar(&r.revision, "revision", "", "Revision of the copied package.")
58-
58+
r.Command.Flags().StringVar(&r.revision, "revision", "", "Revision of the copied package.")
5959
return r
6060
}
6161

@@ -85,12 +85,6 @@ func (r *runner) preRunE(cmd *cobra.Command, args []string) error {
8585
return errors.E(op, fmt.Errorf("too many arguments; SOURCE_PACKAGE is the only accepted positional arguments"))
8686
}
8787

88-
// TODO(natasha41575): This is temporarily required until we can set a default value for the revision. Now that we are disallowing
89-
// package name changes or editing outside the repository, the copy needs to have a new revision number.
90-
if r.revision == "" {
91-
return errors.E(op, fmt.Errorf("--revision is a required flag"))
92-
}
93-
9488
r.copy.Source = &porchapi.PackageRevisionRef{
9589
Name: args[0],
9690
}
@@ -131,24 +125,98 @@ func (r *runner) getPackageRevisionSpec() (*porchapi.PackageRevisionSpec, error)
131125
return nil, err
132126
}
133127

134-
result := porchapi.PackageRevision{}
128+
packageRevision := porchapi.PackageRevision{}
135129
err = restClient.
136130
Get().
137131
Namespace(*r.cfg.Namespace).
138132
Resource("packagerevisions").
139133
Name(r.copy.Source.Name).
140134
Do(r.ctx).
141-
Into(&result)
135+
Into(&packageRevision)
142136
if err != nil {
143137
return nil, err
144138
}
145139

146-
// TODO(natasha41575): Set a default revision of "latest + 1"
140+
if r.revision == "" {
141+
var err error
142+
r.revision, err = r.defaultPackageRevision(
143+
packageRevision.Spec.PackageName,
144+
packageRevision.Spec.RepositoryName,
145+
restClient,
146+
)
147+
if err != nil {
148+
return nil, err
149+
}
150+
}
151+
147152
spec := &porchapi.PackageRevisionSpec{
148-
PackageName: result.Spec.PackageName,
153+
PackageName: packageRevision.Spec.PackageName,
149154
Revision: r.revision,
150-
RepositoryName: result.Spec.RepositoryName,
155+
RepositoryName: packageRevision.Spec.RepositoryName,
151156
}
152157
spec.Tasks = []porchapi.Task{{Type: porchapi.TaskTypeEdit, Edit: &r.copy}}
153158
return spec, nil
154159
}
160+
161+
// defaultPackageRevision attempts to return a default package revision number
162+
// of "latest + 1" given a package name, repository, and namespace. It only
163+
// understands revisions following `v[0-9]+` formats.
164+
func (r *runner) defaultPackageRevision(packageName, repository string, restClient rest.Interface) (string, error) {
165+
// get all package revisions
166+
packageRevisionList := porchapi.PackageRevisionList{}
167+
err := restClient.
168+
Get().
169+
Namespace(*r.cfg.Namespace).
170+
Resource("packagerevisions").
171+
Do(r.ctx).
172+
Into(&packageRevisionList)
173+
if err != nil {
174+
return "", err
175+
}
176+
177+
var latestRevision string
178+
allRevisions := make(map[string]bool) // this is a map for quick access
179+
180+
for _, rev := range packageRevisionList.Items {
181+
if packageName != rev.Spec.PackageName ||
182+
repository != rev.Spec.RepositoryName {
183+
continue
184+
}
185+
186+
if latest, ok := rev.Labels[porchapi.LatestPackageRevisionKey]; ok {
187+
if latest == porchapi.LatestPackageRevisionValue {
188+
latestRevision = rev.Spec.Revision
189+
}
190+
}
191+
allRevisions[rev.Spec.Revision] = true
192+
}
193+
if latestRevision == "" {
194+
return "", fmt.Errorf("no published packages exist; explicit --revision flag is required")
195+
}
196+
197+
next, err := nextRevisionNumber(latestRevision)
198+
if err != nil {
199+
return "", err
200+
}
201+
if _, ok := allRevisions[next]; ok {
202+
return "", fmt.Errorf("default revision %q already exists; explicit --revision flag is required", next)
203+
}
204+
return next, err
205+
}
206+
207+
func nextRevisionNumber(latestRevision string) (string, error) {
208+
match, err := regexp.MatchString("^v[0-9]+$", latestRevision)
209+
if err != nil {
210+
return "", err
211+
}
212+
if !match {
213+
return "", fmt.Errorf("could not understand format of latest revision %q; explicit --revision flag is required", latestRevision)
214+
}
215+
i, err := strconv.Atoi(latestRevision[1:])
216+
if err != nil {
217+
return "", err
218+
}
219+
i++
220+
next := "v" + strconv.Itoa(i)
221+
return next, nil
222+
}

0 commit comments

Comments
 (0)