feat!: use key&value for Labels (#472)

* feat!: use key/value for labels

Signed-off-by: Zbynek Roubalik <zroubali@redhat.com>

* fix typo

Signed-off-by: Zbynek Roubalik <zroubali@redhat.com>
This commit is contained in:
Zbynek Roubalik 2021-08-11 19:12:00 +02:00 committed by GitHub
parent c7332ffb7b
commit 55696811e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 313 additions and 257 deletions

View File

@ -175,7 +175,7 @@ func runAddEnvsPrompt(ctx context.Context, f fn.Function) (err error) {
return
}
newEnv := fn.Pair{}
newEnv := fn.Env{}
switch selectedOption {
// SECTION - add new Environment variable with the specified value
@ -403,7 +403,7 @@ func runRemoveEnvsPrompt(f fn.Function) (err error) {
return
}
var newEnvs fn.Pairs
var newEnvs fn.Envs
removed := false
for i, e := range f.Envs {
if e.String() == selectedEnv {

View File

@ -152,17 +152,17 @@ func runAddLabelsPrompt(ctx context.Context, f fn.Function) (err error) {
return
}
newPair := fn.Pair{}
newPair := fn.Label{}
switch selectedOption {
// SECTION - add new label with the specified value
case optionLabelValue:
qs := []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "Please specify the label name:"},
Name: "key",
Prompt: &survey.Input{Message: "Please specify the label key:"},
Validate: func(val interface{}) error {
return utils.ValidateLabelName(val.(string))
return utils.ValidateLabelKey(val.(string))
},
},
{
@ -173,7 +173,7 @@ func runAddLabelsPrompt(ctx context.Context, f fn.Function) (err error) {
}},
}
answers := struct {
Name string
Key string
Value string
}{}
@ -185,17 +185,17 @@ func runAddLabelsPrompt(ctx context.Context, f fn.Function) (err error) {
return
}
newPair.Name = &answers.Name
newPair.Key = &answers.Key
newPair.Value = &answers.Value
// SECTION - add new label with value from a local environment variable
case optionLabelLocal:
qs := []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "Please specify the label name:"},
Name: "key",
Prompt: &survey.Input{Message: "Please specify the label key:"},
Validate: func(val interface{}) error {
return utils.ValidateLabelName(val.(string))
return utils.ValidateLabelKey(val.(string))
},
},
{
@ -207,7 +207,7 @@ func runAddLabelsPrompt(ctx context.Context, f fn.Function) (err error) {
},
}
answers := struct {
Name string
Key string
Value string
}{}
@ -224,7 +224,7 @@ func runAddLabelsPrompt(ctx context.Context, f fn.Function) (err error) {
}
value := fmt.Sprintf("{{ env:%s }}", answers.Value)
newPair.Name = &answers.Name
newPair.Key = &answers.Key
newPair.Value = &value
}
@ -268,7 +268,7 @@ func runRemoveLabelsPrompt(f fn.Function) (err error) {
return
}
var newLabels fn.Pairs
var newLabels fn.Labels
removed := false
for i, e := range f.Labels {
if e.String() == selectedLabel {

View File

@ -276,7 +276,7 @@ func envFromCmd(cmd *cobra.Command) (*util.OrderedMap, []string, error) {
return util.NewOrderedMap(), []string{}, nil
}
func mergeEnvs(envs fn.Pairs, envToUpdate *util.OrderedMap, envToRemove []string) (fn.Pairs, error) {
func mergeEnvs(envs fn.Envs, envToUpdate *util.OrderedMap, envToRemove []string) (fn.Envs, error) {
updated := sets.NewString()
for i := range envs {
@ -294,7 +294,7 @@ func mergeEnvs(envs fn.Pairs, envToUpdate *util.OrderedMap, envToRemove []string
if !updated.Has(name) {
n := name
v := value
envs = append(envs, fn.Pair{Name: &n, Value: &v})
envs = append(envs, fn.Env{Name: &n, Value: &v})
}
}
@ -309,7 +309,7 @@ func mergeEnvs(envs fn.Pairs, envToUpdate *util.OrderedMap, envToRemove []string
errMsg := fn.ValidateEnvs(envs)
if len(errMsg) > 0 {
return fn.Pairs{}, fmt.Errorf(strings.Join(errMsg, "\n"))
return fn.Envs{}, fmt.Errorf(strings.Join(errMsg, "\n"))
}
return envs, nil

View File

@ -18,77 +18,77 @@ func Test_mergeEnvMaps(t *testing.T) {
v2 := "y"
type args struct {
envs fn.Pairs
envs fn.Envs
toUpdate *util.OrderedMap
toRemove []string
}
tests := []struct {
name string
args args
want fn.Pairs
want fn.Envs
}{
{
"add new var to empty list",
args{
fn.Pairs{},
fn.Envs{},
util.NewOrderedMapWithKVStrings([][]string{{a, v1}}),
[]string{},
},
fn.Pairs{fn.Pair{Name: &a, Value: &v1}},
fn.Envs{fn.Env{Name: &a, Value: &v1}},
},
{
"add new var",
args{
fn.Pairs{fn.Pair{Name: &b, Value: &v2}},
fn.Envs{fn.Env{Name: &b, Value: &v2}},
util.NewOrderedMapWithKVStrings([][]string{{a, v1}}),
[]string{},
},
fn.Pairs{fn.Pair{Name: &b, Value: &v2}, fn.Pair{Name: &a, Value: &v1}},
fn.Envs{fn.Env{Name: &b, Value: &v2}, fn.Env{Name: &a, Value: &v1}},
},
{
"update var",
args{
fn.Pairs{fn.Pair{Name: &a, Value: &v1}},
fn.Envs{fn.Env{Name: &a, Value: &v1}},
util.NewOrderedMapWithKVStrings([][]string{{a, v2}}),
[]string{},
},
fn.Pairs{fn.Pair{Name: &a, Value: &v2}},
fn.Envs{fn.Env{Name: &a, Value: &v2}},
},
{
"update multiple vars",
args{
fn.Pairs{fn.Pair{Name: &a, Value: &v1}, fn.Pair{Name: &b, Value: &v2}},
fn.Envs{fn.Env{Name: &a, Value: &v1}, fn.Env{Name: &b, Value: &v2}},
util.NewOrderedMapWithKVStrings([][]string{{a, v2}, {b, v1}}),
[]string{},
},
fn.Pairs{fn.Pair{Name: &a, Value: &v2}, fn.Pair{Name: &b, Value: &v1}},
fn.Envs{fn.Env{Name: &a, Value: &v2}, fn.Env{Name: &b, Value: &v1}},
},
{
"remove var",
args{
fn.Pairs{fn.Pair{Name: &a, Value: &v1}},
fn.Envs{fn.Env{Name: &a, Value: &v1}},
util.NewOrderedMap(),
[]string{a},
},
fn.Pairs{},
fn.Envs{},
},
{
"remove multiple vars",
args{
fn.Pairs{fn.Pair{Name: &a, Value: &v1}, fn.Pair{Name: &b, Value: &v2}},
fn.Envs{fn.Env{Name: &a, Value: &v1}, fn.Env{Name: &b, Value: &v2}},
util.NewOrderedMap(),
[]string{a, b},
},
fn.Pairs{},
fn.Envs{},
},
{
"update and remove vars",
args{
fn.Pairs{fn.Pair{Name: &a, Value: &v1}, fn.Pair{Name: &b, Value: &v2}},
fn.Envs{fn.Env{Name: &a, Value: &v1}, fn.Env{Name: &b, Value: &v2}},
util.NewOrderedMapWithKVStrings([][]string{{a, v2}}),
[]string{b},
},
fn.Pairs{fn.Pair{Name: &a, Value: &v2}},
fn.Envs{fn.Env{Name: &a, Value: &v2}},
},
}
for _, tt := range tests {

View File

@ -42,13 +42,13 @@ func (v Volume) String() string {
return ""
}
type Pairs []Pair
type Pair struct {
Name *string `yaml:"name,omitempty"`
type Envs []Env
type Env struct {
Name *string `yaml:"name,omitempty" jsonschema:"pattern=^[-._a-zA-Z][-._a-zA-Z0-9]*$"`
Value *string `yaml:"value"`
}
func (e Pair) String() string {
func (e Env) String() string {
if e.Name == nil && e.Value != nil {
match := regWholeSecret.FindStringSubmatch(*e.Value)
if len(match) == 2 {
@ -61,18 +61,40 @@ func (e Pair) String() string {
} else if e.Name != nil && e.Value != nil {
match := regKeyFromSecret.FindStringSubmatch(*e.Value)
if len(match) == 3 {
return fmt.Sprintf("Entry \"%s\" with value set from key \"%s\" from Secret \"%s\"", *e.Name, match[2], match[1])
return fmt.Sprintf("Env \"%s\" with value set from key \"%s\" from Secret \"%s\"", *e.Name, match[2], match[1])
}
match = regKeyFromConfigMap.FindStringSubmatch(*e.Value)
if len(match) == 3 {
return fmt.Sprintf("Entry \"%s\" with value set from key \"%s\" from ConfigMap \"%s\"", *e.Name, match[2], match[1])
return fmt.Sprintf("Env \"%s\" with value set from key \"%s\" from ConfigMap \"%s\"", *e.Name, match[2], match[1])
}
match = regLocalEnv.FindStringSubmatch(*e.Value)
if len(match) == 2 {
return fmt.Sprintf("Entry \"%s\" with value set from local env variable \"%s\"", *e.Name, match[1])
return fmt.Sprintf("Env \"%s\" with value set from local env variable \"%s\"", *e.Name, match[1])
}
return fmt.Sprintf("Entry \"%s\" with value \"%s\"", *e.Name, *e.Value)
return fmt.Sprintf("Env \"%s\" with value \"%s\"", *e.Name, *e.Value)
}
return ""
}
type Labels []Label
type Label struct {
// Key consist of optional prefix part (ended by '/') and name part
// Prefix part validation pattern: [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
// Name part validation pattern: ([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]
Key *string `yaml:"key" jsonschema:"pattern=^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\\/)?([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$"`
Value *string `yaml:"value,omitempty" jsonschema:"pattern=^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$"`
}
func (l Label) String() string {
if l.Key != nil && l.Value == nil {
return fmt.Sprintf("Label with key \"%s\"", *l.Key)
} else if l.Key != nil && l.Value != nil {
match := regLocalEnv.FindStringSubmatch(*l.Value)
if len(match) == 2 {
return fmt.Sprintf("Label with key \"%s\" and value set from local env variable \"%s\"", *l.Key, match[1])
}
return fmt.Sprintf("Label with key \"%s\" and value \"%s\"", *l.Key, *l.Value)
}
return ""
}
@ -117,10 +139,10 @@ type Config struct {
Builder string `yaml:"builder"`
BuilderMap map[string]string `yaml:"builderMap"`
Volumes Volumes `yaml:"volumes"`
Envs Pairs `yaml:"envs"`
Envs Envs `yaml:"envs"`
Annotations map[string]string `yaml:"annotations"`
Options Options `yaml:"options"`
Labels Pairs `yaml:"labels"`
Labels Labels `yaml:"labels"`
// Add new values to the toConfig/fromConfig functions.
}
@ -307,7 +329,7 @@ func validateVolumes(volumes Volumes) (errors []string) {
// - name: EXAMPLE4
// value: {{ configMap:configMapName:key }} # ENV from a key in configMap
// - value: {{ configMap:configMapName }} # all key-pair values from configMap are set as ENV
func ValidateEnvs(envs Pairs) (errors []string) {
func ValidateEnvs(envs Envs) (errors []string) {
for i, env := range envs {
if env.Name == nil && env.Value == nil {
@ -347,36 +369,38 @@ func ValidateEnvs(envs Pairs) (errors []string) {
// Returns array of error messages, empty if no errors are found
//
// Allowed settings:
// - name: EXAMPLE1 # label directly from a value
// - key: EXAMPLE1 # label directly from a value
// value: value1
// - name: EXAMPLE2 # label from the local ENV var
// - key: EXAMPLE2 # label from the local ENV var
// value: {{ env:MY_ENV }}
func ValidateLabels(labels Pairs) (errors []string) {
func ValidateLabels(labels Labels) (errors []string) {
for i, label := range labels {
if label.Name == nil && label.Value == nil {
if label.Key == nil && label.Value == nil {
errors = append(errors, fmt.Sprintf("label entry #%d is not properly set", i))
} else if label.Value == nil {
errors = append(errors, fmt.Sprintf("label entry #%d is missing value field, only name '%s' is set", i, *label.Name))
} else if label.Key == nil && label.Value != nil {
errors = append(errors, fmt.Sprintf("label entry #%d is missing key field, only value '%s' is set", i, *label.Value))
} else {
if err := utils.ValidateLabelName(*label.Name); err != nil {
errors = append(errors, fmt.Sprintf("label entry #%d has invalid name or value set: %q %q; %s", i, *label.Name, *label.Value, err.Error()))
} else if err := utils.ValidateLabelValue(*label.Value); err != nil {
errors = append(errors, fmt.Sprintf("label entry #%d has invalid name or value set: %q %q; %s", i, *label.Name, *label.Value, err.Error()))
if err := utils.ValidateLabelKey(*label.Key); err != nil {
errors = append(errors, fmt.Sprintf("label entry #%d has invalid key set: %q; %s", i, *label.Key, err.Error()))
}
if label.Value != nil {
if err := utils.ValidateLabelValue(*label.Value); err != nil {
errors = append(errors, fmt.Sprintf("label entry #%d has invalid value set: %q; %s", i, *label.Value, err.Error()))
}
if strings.HasPrefix(*label.Value, "{{") {
// ENV from the local ENV var; {{ env:MY_ENV }}
if !regLocalEnv.MatchString(*label.Value) {
errors = append(errors,
fmt.Sprintf(
"label entry #%d with name '%s' has invalid value field set, it has '%s', but allowed is only '{{ env:MY_ENV }}'",
i, *label.Name, *label.Value))
} else {
match := regLocalEnv.FindStringSubmatch(*label.Value)
value := os.Getenv(match[1])
if err := utils.ValidateLabelValue(value); err != nil {
errors = append(errors, fmt.Sprintf("label entry #%d with name '%s' has invalid value when the environment is evaluated: '%s': %s", i, *label.Name, value, err.Error()))
if strings.HasPrefix(*label.Value, "{{") {
// ENV from the local ENV var; {{ env:MY_ENV }}
if !regLocalEnv.MatchString(*label.Value) {
errors = append(errors,
fmt.Sprintf(
"label entry #%d with key '%s' has invalid value field set, it has '%s', but allowed is only '{{ env:MY_ENV }}'",
i, *label.Key, *label.Value))
} else {
match := regLocalEnv.FindStringSubmatch(*label.Value)
value := os.Getenv(match[1])
if err := utils.ValidateLabelValue(value); err != nil {
errors = append(errors, fmt.Sprintf("label entry #%d with key '%s' has invalid value when the environment is evaluated: '%s': %s", i, *label.Key, value, err.Error()))
}
}
}
}

View File

@ -180,13 +180,13 @@ func Test_validateEnvs(t *testing.T) {
tests := []struct {
name string
envs Pairs
envs Envs
errs int
}{
{
"correct entry - single env with value",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &value,
},
@ -195,8 +195,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - missing value",
Pairs{
Pair{
Envs{
Env{
Name: &name,
},
},
@ -204,8 +204,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - invalid name",
Pairs{
Pair{
Envs{
Env{
Name: &incorrectName,
Value: &value,
},
@ -214,8 +214,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - invalid name2",
Pairs{
Pair{
Envs{
Env{
Name: &incorrectName2,
Value: &value,
},
@ -224,12 +224,12 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - multiple envs with value",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &value,
},
Pair{
Env{
Name: &name2,
Value: &value2,
},
@ -238,11 +238,11 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - mmissing value - multiple envs",
Pairs{
Pair{
Envs{
Env{
Name: &name,
},
Pair{
Env{
Name: &name2,
},
},
@ -250,8 +250,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - single env with value Local env",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueLocalEnv,
},
@ -260,16 +260,16 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - multiple envs with value Local env",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueLocalEnv,
},
Pair{
Env{
Name: &name,
Value: &valueLocalEnv2,
},
Pair{
Env{
Name: &name,
Value: &valueLocalEnv3,
},
@ -278,20 +278,20 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - multiple envs with value Local env",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueLocalEnv,
},
Pair{
Env{
Name: &name,
Value: &valueLocalEnvIncorrect,
},
Pair{
Env{
Name: &name,
Value: &valueLocalEnvIncorrect2,
},
Pair{
Env{
Name: &name,
Value: &valueLocalEnvIncorrect3,
},
@ -300,8 +300,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - single secret with key",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueSecretKey,
},
@ -310,8 +310,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - single configMap with key",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueConfigMapKey,
},
@ -320,16 +320,16 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - multiple secrets with key",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueSecretKey,
},
Pair{
Env{
Name: &name,
Value: &valueSecretKey2,
},
Pair{
Env{
Name: &name,
Value: &valueSecretKey3,
},
@ -338,12 +338,12 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - both secret and configmap with key",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueSecretKey,
},
Pair{
Env{
Name: &name,
Value: &valueConfigMapKey,
},
@ -352,8 +352,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - single secret with key",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueSecretKeyIncorrect,
},
@ -362,20 +362,20 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - mutliple secrets with key",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &valueSecretKey,
},
Pair{
Env{
Name: &name,
Value: &valueSecretKeyIncorrect,
},
Pair{
Env{
Name: &name,
Value: &valueSecretKeyIncorrect2,
},
Pair{
Env{
Name: &name,
Value: &valueSecretKeyIncorrect3,
},
@ -384,8 +384,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - single whole secret",
Pairs{
Pair{
Envs{
Env{
Value: &valueSecret,
},
},
@ -393,8 +393,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - single whole configMap",
Pairs{
Pair{
Envs{
Env{
Value: &valueConfigMap,
},
},
@ -402,14 +402,14 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - multiple whole secret",
Pairs{
Pair{
Envs{
Env{
Value: &valueSecret,
},
Pair{
Env{
Value: &valueSecret2,
},
Pair{
Env{
Value: &valueSecret3,
},
},
@ -417,11 +417,11 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - both whole secret and configMap",
Pairs{
Pair{
Envs{
Env{
Value: &valueSecret,
},
Pair{
Env{
Value: &valueConfigMap,
},
},
@ -429,8 +429,8 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - single whole secret",
Pairs{
Pair{
Envs{
Env{
Value: &value,
},
},
@ -438,29 +438,29 @@ func Test_validateEnvs(t *testing.T) {
},
{
"incorrect entry - multiple whole secret",
Pairs{
Pair{
Envs{
Env{
Value: &valueSecretIncorrect,
},
Pair{
Env{
Value: &valueSecretIncorrect2,
},
Pair{
Env{
Value: &valueSecretIncorrect3,
},
Pair{
Env{
Value: &value,
},
Pair{
Env{
Value: &valueLocalEnv,
},
Pair{
Env{
Value: &valueLocalEnv2,
},
Pair{
Env{
Value: &valueLocalEnv3,
},
Pair{
Env{
Value: &valueSecret,
},
},
@ -468,52 +468,52 @@ func Test_validateEnvs(t *testing.T) {
},
{
"correct entry - all combinations",
Pairs{
Pair{
Envs{
Env{
Name: &name,
Value: &value,
},
Pair{
Env{
Name: &name2,
Value: &value2,
},
Pair{
Env{
Name: &name,
Value: &valueLocalEnv,
},
Pair{
Env{
Name: &name,
Value: &valueLocalEnv2,
},
Pair{
Env{
Name: &name,
Value: &valueLocalEnv3,
},
Pair{
Env{
Value: &valueSecret,
},
Pair{
Env{
Value: &valueSecret2,
},
Pair{
Env{
Value: &valueSecret3,
},
Pair{
Env{
Value: &valueConfigMap,
},
Pair{
Env{
Name: &name,
Value: &valueSecretKey,
},
Pair{
Env{
Name: &name,
Value: &valueSecretKey2,
},
Pair{
Env{
Name: &name,
Value: &valueSecretKey3,
},
Pair{
Env{
Name: &name,
Value: &valueConfigMapKey,
},
@ -534,15 +534,15 @@ func Test_validateEnvs(t *testing.T) {
func Test_validateLabels(t *testing.T) {
name := "name"
name2 := "name-two"
name3 := "prefix.io/name3"
key := "name"
key2 := "name-two"
key3 := "prefix.io/name3"
value := "value"
value2 := "value2"
value3 := "value3"
incorrectName := ",foo"
incorrectName2 := ":foo"
incorrectKey := ",foo"
incorrectKey2 := ":foo"
incorrectValue := ":foo"
valueLocalEnv := "{{ env:MY_ENV }}"
@ -559,15 +559,15 @@ func Test_validateLabels(t *testing.T) {
valueLocalEnv4 := "{{env:GOOD_EXAMPLE}}"
tests := []struct {
name string
labels Pairs
key string
labels Labels
errs int
}{
{
"correct entry - single label with value",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &value,
},
},
@ -575,37 +575,48 @@ func Test_validateLabels(t *testing.T) {
},
{
"correct entry - prefixed label with value",
Pairs{
Pair{
Name: &name3,
Labels{
Label{
Key: &key3,
Value: &value3,
},
},
0,
}, {
"incorrect entry - missing value",
Pairs{
Pair{
Name: &name,
"incorrect entry - missing key",
Labels{
Label{
Value: &value,
},
},
1,
},{
"incorrect entry - missing multiple keys",
Labels{
Label{
Value: &value,
},
Label{
Value: &value2,
},
},
2,
},
{
"incorrect entry - invalid name",
Pairs{
Pair{
Name: &incorrectName,
"incorrect entry - invalid key",
Labels{
Label{
Key: &incorrectKey,
Value: &value,
},
},
1,
},
{
"incorrect entry - invalid name2",
Pairs{
Pair{
Name: &incorrectName2,
"incorrect entry - invalid key2",
Labels{
Label{
Key: &incorrectKey2,
Value: &value,
},
},
@ -613,9 +624,9 @@ func Test_validateLabels(t *testing.T) {
},
{
"incorrect entry - invalid value",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &incorrectValue,
},
},
@ -623,35 +634,35 @@ func Test_validateLabels(t *testing.T) {
},
{
"correct entry - multiple labels with value",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &value,
},
Pair{
Name: &name2,
Label{
Key: &key2,
Value: &value2,
},
},
0,
},
{
"incorrect entry - missing value - multiple labels",
Pairs{
Pair{
Name: &name,
"correct entry - missing value - multiple labels",
Labels{
Label{
Key: &key,
},
Pair{
Name: &name2,
Label{
Key: &key2,
},
},
2,
0,
},
{
"correct entry - single label with value from local env",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &valueLocalEnv,
},
},
@ -659,17 +670,17 @@ func Test_validateLabels(t *testing.T) {
},
{
"correct entry - multiple labels with values from Local env",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &valueLocalEnv,
},
Pair{
Name: &name,
Label{
Key: &key,
Value: &valueLocalEnv2,
},
Pair{
Name: &name,
Label{
Key: &key,
Value: &valueLocalEnv3,
},
},
@ -677,21 +688,21 @@ func Test_validateLabels(t *testing.T) {
},
{
"incorrect entry - multiple labels with values from Local env",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &valueLocalEnv,
},
Pair{
Name: &name,
Label{
Key: &key,
Value: &valueLocalEnvIncorrect,
},
Pair{
Name: &name,
Label{
Key: &key,
Value: &valueLocalEnvIncorrect2,
},
Pair{
Name: &name,
Label{
Key: &key,
Value: &valueLocalEnvIncorrect3,
},
},
@ -699,18 +710,18 @@ func Test_validateLabels(t *testing.T) {
},
{
"correct entry - good environment variable value",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &valueLocalEnv4,
},
},
0,
}, {
"incorrect entry - bad environment variable value",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &valueLocalEnvIncorrect4,
},
},
@ -718,29 +729,29 @@ func Test_validateLabels(t *testing.T) {
},
{
"correct entry - all combinations",
Pairs{
Pair{
Name: &name,
Labels{
Label{
Key: &key,
Value: &value,
},
Pair{
Name: &name2,
Label{
Key: &key2,
Value: &value2,
},
Pair{
Name: &name3,
Label{
Key: &key3,
Value: &value3,
},
Pair{
Name: &name,
Label{
Key: &key,
Value: &valueLocalEnv,
},
Pair{
Name: &name,
Label{
Key: &key,
Value: &valueLocalEnv2,
},
Pair{
Name: &name,
Label{
Key: &key,
Value: &valueLocalEnv3,
},
},
@ -749,7 +760,7 @@ func Test_validateLabels(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Run(tt.key, func(t *testing.T) {
if got := ValidateLabels(tt.labels); len(got) != tt.errs {
t.Errorf("validateLabels() = %v\n got %d errors but want %d", got, len(got), tt.errs)
}

View File

@ -69,9 +69,9 @@ directly from a value or from a local environment value. Eg. `'{{ env:USER }}'`,
```yaml
labels:
- name: role # (1) label directly from a value
- key: role # (1) label directly from a value
value: backend
- name: author # (2) label from a local environment value
- key: author # (2) label from a local environment value
value: '{{ env:USER }}'
```

View File

@ -56,7 +56,7 @@ type Function struct {
Volumes Volumes
// Env variables to be set
Envs Pairs
Envs Envs
// Map containing user-supplied annotations
// Example: { "division": "finance" }
@ -66,7 +66,7 @@ type Function struct {
Options Options
// Map of user-supplied labels
Labels Pairs
Labels Labels
}
// NewFunction loads a Function from a path on disk. use .Initialized() to determine if

View File

@ -250,9 +250,9 @@ func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.En
// processLabels generates a map of labels as key/value pairs from a function config
// labels:
// - name: EXAMPLE1 # Label directly from a value
// - key: EXAMPLE1 # Label directly from a value
// value: value1
// - name: EXAMPLE2 # Label from the local ENV var
// - key: EXAMPLE2 # Label from the local ENV var
// value: {{ env:MY_ENV }}
func processLabels(f fn.Function) (map[string]string, error) {
labels := map[string]string{
@ -260,7 +260,7 @@ func processLabels(f fn.Function) (map[string]string, error) {
"boson.dev/runtime": f.Runtime,
}
for _, label := range f.Labels {
if label.Name != nil && label.Value != nil {
if label.Key != nil && label.Value != nil {
if strings.HasPrefix(*label.Value, "{{") {
slices := strings.Split(strings.Trim(*label.Value, "{} "), ":")
if len(slices) == 2 {
@ -269,14 +269,16 @@ func processLabels(f fn.Function) (map[string]string, error) {
if err != nil {
return nil, err
}
labels[*label.Name] = localValue
labels[*label.Key] = localValue
continue
}
} else {
// a standard label with key and value, eg. author=alice@example.com
labels[*label.Name] = *label.Value
labels[*label.Key] = *label.Value
continue
}
} else if label.Key != nil && label.Value == nil {
labels[*label.Key] = ""
}
}
@ -295,7 +297,7 @@ func processLabels(f fn.Function) (map[string]string, error) {
// - name: EXAMPLE4
// value: {{ configMap:configMapName:key }} # ENV from a key in ConfigMap
// - value: {{ configMap:configMapName }} # all key-pair values from ConfigMap are set as ENV
func processEnvs(envs fn.Pairs, referencedSecrets, referencedConfigMaps *sets.String) ([]corev1.EnvVar, []corev1.EnvFromSource, error) {
func processEnvs(envs fn.Envs, referencedSecrets, referencedConfigMaps *sets.String) ([]corev1.EnvVar, []corev1.EnvFromSource, error) {
envVars := []corev1.EnvVar{{Name: "BUILT", Value: time.Now().Format("20060102T150405")}}
envFrom := []corev1.EnvFromSource{}

View File

@ -55,7 +55,7 @@
"envs": {
"items": {
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/Pair"
"$ref": "#/definitions/Env"
},
"type": "array"
},
@ -73,7 +73,8 @@
},
"labels": {
"items": {
"$ref": "#/definitions/Pair"
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/Label"
},
"type": "array"
}
@ -81,6 +82,39 @@
"additionalProperties": false,
"type": "object"
},
"Env": {
"required": [
"value"
],
"properties": {
"name": {
"pattern": "^[-._a-zA-Z][-._a-zA-Z0-9]*$",
"type": "string"
},
"value": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"Label": {
"required": [
"key"
],
"properties": {
"key": {
"pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\\/)?([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$",
"type": "string"
},
"value": {
"pattern": "^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$",
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"Options": {
"properties": {
"scale": {
@ -95,21 +129,6 @@
"additionalProperties": false,
"type": "object"
},
"Pair": {
"required": [
"value"
],
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"ResourcesLimitsOptions": {
"properties": {
"cpu": {

View File

@ -44,15 +44,15 @@ func ValidateEnvVarName(name string) error {
return nil
}
// ValidateLabelName validates that the input name is a valid Kubernetes key.
// ValidateLabelKey validates that the input name is a valid Kubernetes key.
// Valid label names have two segments: an optional prefix and name, separated by a slash (/).
// The name segment is required and must be 63 characters or less, beginning and ending with
// an alphanumeric character ([a-z0-9A-Z]) with dashes (-), underscores (_), dots (.), and
// alphanumerics between. The prefix is optional. If specified, the prefix must be a DNS subdomain:
// a series of DNS labels separated by dots (.), not longer than 253 characters in total, followed
// by a slash (/).
func ValidateLabelName(name string) error {
errs := validation.IsQualifiedName(name)
func ValidateLabelKey(key string) error {
errs := validation.IsQualifiedName(key)
if len(errs) > 0 {
return ErrInvalidLabel(errors.New(strings.Join(errs, "")))
}

View File

@ -86,7 +86,7 @@ func TestValidateLabelName(t *testing.T) {
}
for _, c := range cases {
err := ValidateLabelName(c.In)
err := ValidateLabelKey(c.In)
if err != nil && c.Valid {
t.Fatalf("Unexpected error: %v, for '%v'", err, c.In)
}