Skip to content

Commit 82b616d

Browse files
committed
api: Add error handling and test for retrieving a stage's PRR approver
Signed-off-by: Stephen Augustus <[email protected]>
1 parent ce64dc5 commit 82b616d

File tree

7 files changed

+211
-17
lines changed

7 files changed

+211
-17
lines changed

api/approval.go

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,38 @@ func (prr *PRRApproval) Validate() error {
5353
return nil
5454
}
5555

56-
func (prr *PRRApproval) ApproverForStage(stage string) string {
56+
func (prr *PRRApproval) ApproverForStage(stage string) (string, error) {
57+
isValidStage := IsOneOf(stage, ValidStages)
58+
if !isValidStage {
59+
return "", ErrKEPStageIsInvalid(stage)
60+
}
61+
62+
if prr.Alpha == nil && prr.Beta == nil && prr.Stable == nil {
63+
return "", ErrPRRMilestonesAllEmpty
64+
}
65+
5766
switch stage {
5867
case "alpha":
59-
return prr.Alpha.Approver
68+
if prr.Alpha == nil {
69+
return "", ErrPRRMilestoneIsNil
70+
}
71+
72+
return prr.Alpha.Approver, nil
6073
case "beta":
61-
return prr.Beta.Approver
74+
if prr.Beta == nil {
75+
return "", ErrPRRMilestoneIsNil
76+
}
77+
78+
return prr.Beta.Approver, nil
6279
case "stable":
63-
return prr.Stable.Approver
80+
if prr.Stable == nil {
81+
return "", ErrPRRMilestoneIsNil
82+
}
83+
84+
return prr.Stable.Approver, nil
6485
}
6586

66-
return ""
87+
return "", ErrPRRApproverUnknown
6788
}
6889

6990
// TODO(api): Can we refactor the proposal `Milestone` to retrieve this?
@@ -112,13 +133,3 @@ func (p *PRRHandler) Parse(in io.Reader) (*PRRApproval, error) {
112133

113134
return approval, nil
114135
}
115-
116-
func IsOneOf(check string, slice []string) bool {
117-
for _, v := range slice {
118-
if v == check {
119-
return true
120-
}
121-
}
122-
123-
return false
124-
}

api/approval_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
htcp://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package api_test
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/require"
23+
"k8s.io/enhancements/api"
24+
)
25+
26+
var (
27+
prrMilestoneWithApprover = &api.PRRMilestone{
28+
Approver: "@wojtek-t",
29+
}
30+
31+
validAlphaPRR = &api.PRRApproval{
32+
Alpha: prrMilestoneWithApprover,
33+
}
34+
35+
validBetaPRR = &api.PRRApproval{
36+
Beta: prrMilestoneWithApprover,
37+
}
38+
39+
validStablePRR = &api.PRRApproval{
40+
Stable: prrMilestoneWithApprover,
41+
}
42+
43+
invalidPRR = &api.PRRApproval{}
44+
)
45+
46+
func TestPRRApproval_ApproverForStage(t *testing.T) {
47+
testcases := []struct {
48+
name string
49+
stage string
50+
prr *api.PRRApproval
51+
wantApprover string
52+
wantErr bool
53+
}{
54+
{
55+
name: "invalid: incorrect stage",
56+
stage: "badstage",
57+
prr: validAlphaPRR,
58+
wantErr: true,
59+
},
60+
{
61+
name: "invalid: missing approver",
62+
stage: "alpha",
63+
prr: invalidPRR,
64+
wantApprover: "",
65+
wantErr: true,
66+
},
67+
{
68+
name: "valid: alpha",
69+
stage: "alpha",
70+
prr: validAlphaPRR,
71+
wantApprover: "@wojtek-t",
72+
wantErr: false,
73+
},
74+
{
75+
name: "valid: beta",
76+
stage: "beta",
77+
prr: validBetaPRR,
78+
wantApprover: "@wojtek-t",
79+
wantErr: false,
80+
},
81+
{
82+
name: "valid: stable",
83+
stage: "stable",
84+
prr: validStablePRR,
85+
wantApprover: "@wojtek-t",
86+
wantErr: false,
87+
},
88+
}
89+
90+
for _, tc := range testcases {
91+
t.Run(tc.name, func(t *testing.T) {
92+
hasErr := false
93+
approver, err := tc.prr.ApproverForStage(tc.stage)
94+
if err != nil {
95+
hasErr = true
96+
}
97+
98+
require.Equal(t, tc.wantErr, hasErr)
99+
require.Equal(t, tc.wantApprover, approver)
100+
},
101+
)
102+
}
103+
}

api/document.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,14 @@ type Parser struct {
4646

4747
Errors []error
4848
}
49+
50+
// TODO: Can we come up with a better name/location for this?
51+
func IsOneOf(check string, slice []string) bool {
52+
for _, v := range slice {
53+
if v == check {
54+
return true
55+
}
56+
}
57+
58+
return false
59+
}

api/error.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package api
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/pkg/errors"
23+
)
24+
25+
var (
26+
// PRR errors
27+
28+
ErrPRRMilestonesAllEmpty error = errors.New(
29+
"none of the PRR milestones are populated",
30+
)
31+
32+
ErrPRRMilestoneIsNil error = errors.New(
33+
"the selected PRR milestone stage is not populated",
34+
)
35+
36+
ErrPRRApproverUnknown error = errors.New(
37+
"an unknown error occurred while trying to determine a PRR approver",
38+
)
39+
)
40+
41+
// KEP errors
42+
func ErrKEPStageIsInvalid(stage string) error {
43+
return errors.New(
44+
fmt.Sprintf(
45+
"the specified stage (%s) should be one of the following: %v",
46+
stage,
47+
ValidStages,
48+
),
49+
)
50+
}

api/proposal.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ import (
2929
"gopkg.in/yaml.v3"
3030
)
3131

32+
var ValidStages = []string{
33+
"alpha",
34+
"beta",
35+
"stable",
36+
}
37+
3238
type Proposals []*Proposal
3339

3440
func (p *Proposals) AddProposal(proposal *Proposal) {

pkg/kepctl/kepctl.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,15 +415,21 @@ func (c *Client) loadKEPFromYaml(kepPath string) (*api.Proposal, error) {
415415
fmt.Fprintf(c.Err, "WARNING: could not parse prod readiness request for KEP %s: %s\n", p.Number, approval.Error)
416416
}
417417

418-
approver := approval.ApproverForStage(p.Stage)
418+
approver, err := approval.ApproverForStage(p.Stage)
419+
if err != nil {
420+
return nil, errors.Wrapf(err, "getting PRR approver for %s stage", p.Stage)
421+
}
422+
419423
for _, a := range p.PRRApprovers {
420424
if a == approver {
421425
approver = ""
422426
}
423427
}
428+
424429
if approver != "" {
425430
p.PRRApprovers = append(p.PRRApprovers, approver)
426431
}
432+
427433
return &p, nil
428434
}
429435

pkg/kepval/approval.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,14 @@ func ValidatePRR(kep *api.Proposal, h *api.PRRHandler, prrDir string) error {
7171
return errors.Wrapf(prr.Error, "%v has an error", prrFilepath)
7272
}
7373

74-
stagePRRApprover := prr.ApproverForStage(kep.Stage)
74+
stagePRRApprover, err := prr.ApproverForStage(kep.Stage)
75+
if err != nil {
76+
return errors.Wrapf(err, "getting PRR approver for %s stage", kep.Stage)
77+
}
78+
79+
if stagePRRApprover == "" {
80+
return errors.New("PRR approver cannot be empty")
81+
}
7582

7683
if strings.HasPrefix(stagePRRApprover, "@") {
7784
stagePRRApprover = strings.TrimPrefix(stagePRRApprover, "@")

0 commit comments

Comments
 (0)