mirror of https://github.com/kubernetes/kops.git
Rename StringOrSlice to StringOrSet, sort lists
This commit is contained in:
parent
21804bf631
commit
f098401c49
|
@ -60,19 +60,19 @@ func addCertManagerPermissions(b *iam.PolicyBuilder, p *iam.Policy) {
|
|||
Action: stringorslice.Of("route53:ChangeResourceRecordSets",
|
||||
"route53:ListResourceRecordSets",
|
||||
),
|
||||
Resource: stringorslice.Slice(zoneResources),
|
||||
Resource: stringorslice.Set(zoneResources),
|
||||
})
|
||||
|
||||
p.Statement = append(p.Statement, &iam.Statement{
|
||||
Effect: iam.StatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"route53:GetChange"}),
|
||||
Resource: stringorslice.Slice([]string{fmt.Sprintf("arn:%v:route53:::change/*", b.Partition)}),
|
||||
Action: stringorslice.Set([]string{"route53:GetChange"}),
|
||||
Resource: stringorslice.Set([]string{fmt.Sprintf("arn:%v:route53:::change/*", b.Partition)}),
|
||||
})
|
||||
|
||||
wildcard := stringorslice.Slice([]string{"*"})
|
||||
wildcard := stringorslice.Set([]string{"*"})
|
||||
p.Statement = append(p.Statement, &iam.Statement{
|
||||
Effect: iam.StatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"route53:ListHostedZonesByName"}),
|
||||
Action: stringorslice.Set([]string{"route53:ListHostedZonesByName"}),
|
||||
Resource: wildcard,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ func (p *Policy) AddEC2CreateAction(actions, resources []string) {
|
|||
&Statement{
|
||||
Effect: StatementEffectAllow,
|
||||
Action: stringorslice.String("ec2:CreateTags"),
|
||||
Resource: stringorslice.Slice(actualResources),
|
||||
Resource: stringorslice.Set(actualResources),
|
||||
Condition: Condition{
|
||||
"StringEquals": map[string]interface{}{
|
||||
"aws:RequestTag/KubernetesCluster": p.clusterName,
|
||||
|
@ -89,11 +89,11 @@ func (p *Policy) AddEC2CreateAction(actions, resources []string) {
|
|||
|
||||
&Statement{
|
||||
Effect: StatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{
|
||||
Action: stringorslice.Set([]string{
|
||||
"ec2:CreateTags",
|
||||
"ec2:DeleteTags", // aws.go, tag.go
|
||||
}),
|
||||
Resource: stringorslice.Slice(actualResources),
|
||||
Resource: stringorslice.Set(actualResources),
|
||||
Condition: Condition{
|
||||
"Null": map[string]string{
|
||||
"aws:RequestTag/KubernetesCluster": "true",
|
||||
|
@ -176,8 +176,8 @@ type Condition map[string]interface{}
|
|||
type Statement struct {
|
||||
Effect StatementEffect
|
||||
Principal Principal
|
||||
Action stringorslice.StringOrSlice
|
||||
Resource stringorslice.StringOrSlice
|
||||
Action stringorslice.StringOrSet
|
||||
Resource stringorslice.StringOrSet
|
||||
Condition Condition
|
||||
}
|
||||
|
||||
|
@ -600,7 +600,7 @@ func (b *PolicyBuilder) AddS3Permissions(p *Policy) (*Policy, error) {
|
|||
"s3:ListBucket",
|
||||
"s3:ListBucketVersions",
|
||||
),
|
||||
Resource: stringorslice.Slice([]string{
|
||||
Resource: stringorslice.Set([]string{
|
||||
fmt.Sprintf("arn:%v:s3:::%v", p.partition, s3Bucket),
|
||||
}),
|
||||
})
|
||||
|
@ -612,7 +612,7 @@ func (b *PolicyBuilder) AddS3Permissions(p *Policy) (*Policy, error) {
|
|||
func (b *PolicyBuilder) buildS3WriteStatements(p *Policy, iamS3Path string) {
|
||||
p.Statement = append(p.Statement, &Statement{
|
||||
Effect: StatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{
|
||||
Action: stringorslice.Set([]string{
|
||||
"s3:GetObject",
|
||||
"s3:DeleteObject",
|
||||
"s3:DeleteObjectVersion",
|
||||
|
@ -640,7 +640,7 @@ func (b *PolicyBuilder) buildS3GetStatements(p *Policy, iamS3Path string) error
|
|||
|
||||
p.Statement = append(p.Statement, &Statement{
|
||||
Effect: StatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"s3:Get*"}),
|
||||
Action: stringorslice.Set([]string{"s3:Get*"}),
|
||||
Resource: stringorslice.Of(resources...),
|
||||
})
|
||||
}
|
||||
|
@ -803,7 +803,7 @@ func addEtcdManagerPermissions(p *Policy) {
|
|||
Action: stringorslice.Of(
|
||||
"ec2:AttachVolume",
|
||||
),
|
||||
Resource: stringorslice.Slice([]string{"*"}),
|
||||
Resource: stringorslice.Set([]string{"*"}),
|
||||
Condition: Condition{
|
||||
"StringEquals": map[string]string{
|
||||
"aws:ResourceTag/k8s.io/role/master": "1",
|
||||
|
@ -1064,19 +1064,19 @@ func AddDNSControllerPermissions(b *PolicyBuilder, p *Policy) {
|
|||
Action: stringorslice.Of("route53:ChangeResourceRecordSets",
|
||||
"route53:ListResourceRecordSets",
|
||||
"route53:GetHostedZone"),
|
||||
Resource: stringorslice.Slice([]string{fmt.Sprintf("arn:%v:route53:::hostedzone/%v", b.Partition, hostedZoneID)}),
|
||||
Resource: stringorslice.Set([]string{fmt.Sprintf("arn:%v:route53:::hostedzone/%v", b.Partition, hostedZoneID)}),
|
||||
})
|
||||
|
||||
p.Statement = append(p.Statement, &Statement{
|
||||
Effect: StatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"route53:GetChange"}),
|
||||
Resource: stringorslice.Slice([]string{fmt.Sprintf("arn:%v:route53:::change/*", b.Partition)}),
|
||||
Action: stringorslice.Set([]string{"route53:GetChange"}),
|
||||
Resource: stringorslice.Set([]string{fmt.Sprintf("arn:%v:route53:::change/*", b.Partition)}),
|
||||
})
|
||||
|
||||
wildcard := stringorslice.Slice([]string{"*"})
|
||||
wildcard := stringorslice.Set([]string{"*"})
|
||||
p.Statement = append(p.Statement, &Statement{
|
||||
Effect: StatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"route53:ListHostedZones", "route53:ListTagsForResource"}),
|
||||
Action: stringorslice.Set([]string{"route53:ListHostedZones", "route53:ListTagsForResource"}),
|
||||
Resource: wildcard,
|
||||
})
|
||||
}
|
||||
|
@ -1169,10 +1169,10 @@ func addAmazonVPCCNIPermissions(p *Policy) {
|
|||
p.Statement = append(p.Statement,
|
||||
&Statement{
|
||||
Effect: StatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{
|
||||
Action: stringorslice.Set([]string{
|
||||
"ec2:CreateTags",
|
||||
}),
|
||||
Resource: stringorslice.Slice([]string{
|
||||
Resource: stringorslice.Set([]string{
|
||||
strings.Join([]string{"arn:", p.partition, ":ec2:*:*:network-interface/*"}, ""),
|
||||
}),
|
||||
},
|
||||
|
|
|
@ -49,7 +49,7 @@ func TestRoundTrip(t *testing.T) {
|
|||
Action: stringorslice.Of("ec2:DescribeRegions", "ec2:DescribeInstances"),
|
||||
Resource: stringorslice.Of("a", "b"),
|
||||
},
|
||||
JSON: "{\"Action\":[\"ec2:DescribeRegions\",\"ec2:DescribeInstances\"],\"Effect\":\"Deny\",\"Resource\":[\"a\",\"b\"]}",
|
||||
JSON: "{\"Action\":[\"ec2:DescribeInstances\",\"ec2:DescribeRegions\"],\"Effect\":\"Deny\",\"Resource\":[\"a\",\"b\"]}",
|
||||
},
|
||||
{
|
||||
IAM: &Statement{
|
||||
|
|
|
@ -18,45 +18,54 @@ package stringorslice
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// StringOrSlice is a type that holds a []string, but marshals to a []string or a string.
|
||||
type StringOrSlice struct {
|
||||
values []string
|
||||
// StringOrSet is a type that holds a []string, but marshals to a []string or a string.
|
||||
type StringOrSet struct {
|
||||
values sets.Set[string]
|
||||
forceEncodeAsArray bool
|
||||
}
|
||||
|
||||
func (s *StringOrSlice) IsEmpty() bool {
|
||||
func (s *StringOrSet) IsEmpty() bool {
|
||||
return len(s.values) == 0
|
||||
}
|
||||
|
||||
// Slice will build a value that marshals to a JSON array
|
||||
func Slice(v []string) StringOrSlice {
|
||||
return StringOrSlice{values: v, forceEncodeAsArray: true}
|
||||
// Set will build a value that marshals to a JSON array
|
||||
func Set(v []string) StringOrSet {
|
||||
values := sets.Set[string]{}
|
||||
values.Insert(v...)
|
||||
return StringOrSet{values: values, forceEncodeAsArray: true}
|
||||
}
|
||||
|
||||
// Of will build a value that marshals to a JSON array if len(v) > 1,
|
||||
// otherwise to a single string
|
||||
func Of(v ...string) StringOrSlice {
|
||||
func Of(v ...string) StringOrSet {
|
||||
if v == nil {
|
||||
v = []string{}
|
||||
}
|
||||
return StringOrSlice{values: v}
|
||||
values := sets.Set[string]{}
|
||||
values.Insert(v...)
|
||||
return StringOrSet{values: values}
|
||||
}
|
||||
|
||||
// String will build a value that marshals to a single string
|
||||
func String(v string) StringOrSlice {
|
||||
return StringOrSlice{values: []string{v}, forceEncodeAsArray: false}
|
||||
func String(v string) StringOrSet {
|
||||
return StringOrSet{values: sets.New(v), forceEncodeAsArray: false}
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaller interface.
|
||||
func (s *StringOrSlice) UnmarshalJSON(value []byte) error {
|
||||
func (s *StringOrSet) UnmarshalJSON(value []byte) error {
|
||||
if value[0] == '[' {
|
||||
s.forceEncodeAsArray = true
|
||||
if err := json.Unmarshal(value, &s.values); err != nil {
|
||||
var vals []string
|
||||
if err := json.Unmarshal(value, &vals); err != nil {
|
||||
return nil
|
||||
}
|
||||
s.values = sets.New(vals...)
|
||||
return nil
|
||||
}
|
||||
s.forceEncodeAsArray = false
|
||||
|
@ -64,46 +73,39 @@ func (s *StringOrSlice) UnmarshalJSON(value []byte) error {
|
|||
if err := json.Unmarshal(value, &stringValue); err != nil {
|
||||
return err
|
||||
}
|
||||
s.values = []string{stringValue}
|
||||
s.values = sets.New(stringValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string value, or the Itoa of the int value.
|
||||
func (s StringOrSlice) String() string {
|
||||
return strings.Join(s.values, ",")
|
||||
func (s StringOrSet) String() string {
|
||||
return strings.Join(sets.List[string](s.values), ",")
|
||||
}
|
||||
|
||||
func (v *StringOrSlice) Value() []string {
|
||||
return v.values
|
||||
func (v *StringOrSet) Value() []string {
|
||||
vals := sets.List[string](v.values)
|
||||
sort.Strings(vals)
|
||||
return vals
|
||||
}
|
||||
|
||||
func (l StringOrSlice) Equal(r StringOrSlice) bool {
|
||||
if len(l.values) != len(r.values) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(l.values); i++ {
|
||||
if l.values[i] != r.values[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
func (l StringOrSet) Equal(r StringOrSet) bool {
|
||||
return l.values.Equal(r.values)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaller interface.
|
||||
func (v StringOrSlice) MarshalJSON() ([]byte, error) {
|
||||
func (v StringOrSet) MarshalJSON() ([]byte, error) {
|
||||
encodeAsJSONArray := v.forceEncodeAsArray
|
||||
if len(v.values) > 1 {
|
||||
encodeAsJSONArray = true
|
||||
}
|
||||
values := v.values
|
||||
values := v.Value()
|
||||
if values == nil {
|
||||
values = []string{}
|
||||
}
|
||||
if encodeAsJSONArray {
|
||||
return json.Marshal(values)
|
||||
} else if len(v.values) == 1 {
|
||||
s := v.values[0]
|
||||
return json.Marshal(&s)
|
||||
} else if len(values) == 1 {
|
||||
return json.Marshal(&values[0])
|
||||
} else {
|
||||
return json.Marshal(values)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
func TestRoundTrip(t *testing.T) {
|
||||
grid := []struct {
|
||||
Value StringOrSlice
|
||||
Value StringOrSet
|
||||
JSON string
|
||||
}{
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ func TestRoundTrip(t *testing.T) {
|
|||
JSON: "\"a\"",
|
||||
},
|
||||
{
|
||||
Value: Slice([]string{"a"}),
|
||||
Value: Set([]string{"a"}),
|
||||
JSON: "[\"a\"]",
|
||||
},
|
||||
{
|
||||
|
@ -45,7 +45,7 @@ func TestRoundTrip(t *testing.T) {
|
|||
JSON: "[\"a\",\"b\"]",
|
||||
},
|
||||
{
|
||||
Value: Slice([]string{"a", "b"}),
|
||||
Value: Set([]string{"a", "b"}),
|
||||
JSON: "[\"a\",\"b\"]",
|
||||
},
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ func TestRoundTrip(t *testing.T) {
|
|||
JSON: "[]",
|
||||
},
|
||||
{
|
||||
Value: Slice(nil),
|
||||
Value: Set(nil),
|
||||
JSON: "[]",
|
||||
},
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ func TestRoundTrip(t *testing.T) {
|
|||
t.Errorf("Unexpected JSON encoding. Actual=%q, Expected=%q", string(actualJSON), g.JSON)
|
||||
}
|
||||
|
||||
parsed := &StringOrSlice{}
|
||||
parsed := &StringOrSet{}
|
||||
err = json.Unmarshal([]byte(g.JSON), parsed)
|
||||
if err != nil {
|
||||
t.Errorf("error decoding StringOrSlice %s to json: %v", g.JSON, err)
|
||||
|
|
Loading…
Reference in New Issue