Skip to content

Commit

Permalink
k8s/identitybackend: exclude k8s namespace labels from CRD metadata
Browse files Browse the repository at this point in the history
The names of synthetic labels created for k8s namespace labels are
variable in length and may be longer than the maximum 63
characters, which results in validation errors at resource
creation time.

Skip those labels along with non-k8s labels.

Signed-off-by: Romain Lenglet <rlenglet@google.com>
Change-Id: I3f5fe4f32c65787b4d9f00848a13111d36437e5c
  • Loading branch information
rlenglet authored and rolinh committed May 8, 2020
1 parent e3688fb commit bf160f6
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 16 deletions.
12 changes: 10 additions & 2 deletions pkg/k8s/identitybackend/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/cilium/cilium/pkg/allocator"
"github.com/cilium/cilium/pkg/idpool"
k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
clientset "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned"
"github.com/cilium/cilium/pkg/k8s/informer"
Expand All @@ -43,6 +44,11 @@ var (
log = logging.DefaultLogger.WithField(logfields.LogSubsys, "crd-allocator")
)

const (
k8sPrefix = labels.LabelSourceK8s + ":"
k8sNamespaceLabelPrefix = labels.LabelSourceK8s + ":" + k8sConst.PodNamespaceMetaLabels + labels.PathDelimiter
)

func NewCRDBackend(c CRDBackendConfiguration) (allocator.Backend, error) {
return &crdBackend{CRDBackendConfiguration: c}, nil
}
Expand All @@ -68,11 +74,13 @@ func (c *crdBackend) DeleteAllKeys(ctx context.Context) {
// the canonical labels of the identity, but used to ease interaction with the
// CRD object.
func sanitizeK8sLabels(old map[string]string) (selected, skipped map[string]string) {
k8sPrefix := labels.LabelSourceK8s + ":"
skipped = make(map[string]string, len(old))
selected = make(map[string]string, len(old))
for k, v := range old {
if !strings.HasPrefix(k, k8sPrefix) {
// Skip non-k8s labels.
// Skip synthesized labels for k8s namespace labels, since they contain user input which can result in the label
// name being longer than 63 characters.
if !strings.HasPrefix(k, k8sPrefix) || strings.HasPrefix(k, k8sNamespaceLabelPrefix) {
skipped[k] = v
continue // skip non-k8s labels
}
Expand Down
56 changes: 42 additions & 14 deletions pkg/k8s/identitybackend/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"github.com/cilium/cilium/pkg/checker"

. "gopkg.in/check.v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
)

// Hook up gocheck into the "go test" runner.
Expand All @@ -34,36 +36,62 @@ type K8sIdentityBackendSuite struct{}
var _ = Suite(&K8sIdentityBackendSuite{})

func (s *K8sIdentityBackendSuite) TestSanitizeK8sLabels(c *C) {
path := field.NewPath("test", "labels")
testCases := []struct {
input map[string]string
selected map[string]string
skipped map[string]string
input map[string]string
selected map[string]string
skipped map[string]string
validationErrors field.ErrorList
}{
{
input: map[string]string{},
selected: map[string]string{},
skipped: map[string]string{},
input: map[string]string{},
selected: map[string]string{},
skipped: map[string]string{},
validationErrors: field.ErrorList{},
},
{
input: map[string]string{"k8s:foo": "bar"},
selected: map[string]string{"foo": "bar"},
skipped: map[string]string{},
input: map[string]string{"k8s:foo": "bar"},
selected: map[string]string{"foo": "bar"},
skipped: map[string]string{},
validationErrors: field.ErrorList{},
},
{
input: map[string]string{"k8s:foo": "bar", "k8s:abc": "def"},
selected: map[string]string{"foo": "bar", "abc": "def"},
skipped: map[string]string{},
validationErrors: field.ErrorList{},
},
{
input: map[string]string{"k8s:foo": "bar", "k8s:abc": "def", "container:something": "else"},
selected: map[string]string{"foo": "bar", "abc": "def"},
skipped: map[string]string{"container:something": "else"},
validationErrors: field.ErrorList{},
},
{
input: map[string]string{"k8s:foo": "bar", "k8s:abc": "def"},
selected: map[string]string{"foo": "bar", "abc": "def"},
input: map[string]string{"k8s:some.really.really.really.really.really.really.really.long.label.name": "someval"},
selected: map[string]string{"some.really.really.really.really.really.really.really.long.label.name": "someval"},
skipped: map[string]string{},
validationErrors: field.ErrorList{
&field.Error{
Type: "FieldValueInvalid",
Field: "test.labels",
BadValue: "some.really.really.really.really.really.really.really.long.label.name",
Detail: "name part must be no more than 63 characters",
},
},
},
{
input: map[string]string{"k8s:foo": "bar", "k8s:abc": "def", "container:something": "else"},
selected: map[string]string{"foo": "bar", "abc": "def"},
skipped: map[string]string{"container:something": "else"},
input: map[string]string{"k8s:io.cilium.k8s.namespace.labels.some.really.really.long.namespace.label.name": "someval"},
selected: map[string]string{},
skipped: map[string]string{"k8s:io.cilium.k8s.namespace.labels.some.really.really.long.namespace.label.name": "someval"},
validationErrors: field.ErrorList{},
},
}

for _, test := range testCases {
selected, skipped := sanitizeK8sLabels(test.input)
c.Assert(selected, checker.DeepEquals, test.selected)
c.Assert(skipped, checker.DeepEquals, test.skipped)
c.Assert(validation.ValidateLabels(selected, path), checker.DeepEquals, test.validationErrors)
}
}

0 comments on commit bf160f6

Please sign in to comment.