Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ require (

// points to temporary-watch-reduction-patch-1.21 to pick up k/k/pull/101102 - please remove it once the pr merges and a new Z release is cut
replace k8s.io/apiserver => github.com/openshift/kubernetes-apiserver v0.0.0-20210419140141-620426e63a99

replace github.com/openshift/api => github.com/staebler/api v0.0.0-20210421013259-487a3ae5573d
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -416,10 +416,6 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20191031171055-b133feaeeb2e/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/openshift/api v0.0.0-20201214114959-164a2fb63b5f/go.mod h1:aqU5Cq+kqKKPbDMqxo9FojgDeSpNJI7iuskjXjtojDg=
github.com/openshift/api v0.0.0-20210105115604-44119421ec6b/go.mod h1:aqU5Cq+kqKKPbDMqxo9FojgDeSpNJI7iuskjXjtojDg=
github.com/openshift/api v0.0.0-20210415092137-8c78458f83d9 h1:TudJ23vtDe4zyFep9xdw1baNkZ6AyEGSkrzUzws9C0c=
github.com/openshift/api v0.0.0-20210415092137-8c78458f83d9/go.mod h1:dZ4kytOo3svxJHNYd0J55hwe/6IQG5gAUHUE0F3Jkio=
github.com/openshift/build-machinery-go v0.0.0-20200917070002-f171684f77ab/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE=
github.com/openshift/build-machinery-go v0.0.0-20210209125900-0da259a2c359 h1:ehSDsWQiUVzJZrSEXMC7ceV9JIPEyTYqrpqu3m4Wa08=
github.com/openshift/build-machinery-go v0.0.0-20210209125900-0da259a2c359/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE=
Expand Down Expand Up @@ -507,6 +503,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/staebler/api v0.0.0-20210421013259-487a3ae5573d h1:O7vGIbmd+jDXsyIt5zY4szBgScDK12EG1l+dyYlLA/U=
github.com/staebler/api v0.0.0-20210421013259-487a3ae5573d/go.mod h1:dZ4kytOo3svxJHNYd0J55hwe/6IQG5gAUHUE0F3Jkio=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
Expand Down
9 changes: 9 additions & 0 deletions pkg/operator/aws_resource_tags/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
reviewers:
- e-tienne
- jhixson74
- jstuever
- mtnbikenc
- patrickdillon
- rna-afk
- staebler
approvers:
149 changes: 149 additions & 0 deletions pkg/operator/aws_resource_tags/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package aws_resource_tags

import (
"context"
"fmt"
"regexp"
"time"

configv1 "github.com/openshift/api/config/v1"
configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
configv1listers "github.com/openshift/client-go/config/listers/config/v1"
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/events"
operatorv1helpers "github.com/openshift/library-go/pkg/operator/v1helpers"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/cache"
)

// tagRegex is used to check that the keys and values of a tag contain only valid characters
var tagRegex = regexp.MustCompile(`^[0-9A-Za-z_.:/=+-@]*$`)
// kubernetesNamespaceRegex is used to check that a tag key is not in the kubernetes.io namespace
var kubernetesNamespaceRegex = regexp.MustCompile(`^([^/]*\.)?kubernetes.io/`)
// openshiftNamespaceRegex is used to check that a tag key is not in the openshift.io namespace
var openshiftNamespaceRegex = regexp.MustCompile(`^([^/]*\.)?openshift.io/`)

// AWSResourceTagsController syncs the AWS resource tags from the spec of the `infrastructure.config.openshift.io/v1`
// `cluster` object to the status.
type AWSResourceTagsController struct {
infraClient configv1client.InfrastructureInterface
infraLister configv1listers.InfrastructureLister
}

// NewController returns a AWSResourceTagsController
func NewController(operatorClient operatorv1helpers.OperatorClient,
infraClient configv1client.InfrastructuresGetter, infraLister configv1listers.InfrastructureLister, infraInformer cache.SharedIndexInformer,
recorder events.Recorder) factory.Controller {
c := &AWSResourceTagsController{
infraClient: infraClient.Infrastructures(),
infraLister: infraLister,
}
return factory.New().
WithInformers(
operatorClient.Informer(),
infraInformer,
).
WithSync(c.sync).
WithSyncDegradedOnError(operatorClient).
ResyncEvery(time.Minute).
ToController("AWSResourceTagsController", recorder)
}

func (c AWSResourceTagsController) sync(ctx context.Context, syncCtx factory.SyncContext) error {
obji, err := c.infraLister.Get("cluster")
if errors.IsNotFound(err) {
syncCtx.Recorder().Warningf("AWSResourceTagsController", "Required infrastructures.%s/cluster not found", configv1.GroupName)
return nil
}
if err != nil {
return err
}

currentInfra := obji.DeepCopy()

var desiredResourceTags []configv1.AWSResourceTag
if awsSpec := currentInfra.Spec.PlatformSpec.AWS; awsSpec != nil {
keys := sets.NewString()
desiredResourceTags = make([]configv1.AWSResourceTag, 0, len(awsSpec.ResourceTags))
for _, tag := range awsSpec.ResourceTags {
if err := validateTag(tag); err != nil {
syncCtx.Recorder().Warningf("AWSResourceTagsController", "The resource tag with key=%q and value=%q is invalid: %v", tag.Key, tag.Value, err)
continue
}
if keys.Has(tag.Key) {
syncCtx.Recorder().Warningf("AWSResourceTagsController", "The resource tag with key=%q and value=%q is a duplicate", tag.Key, tag.Value)
continue
}
keys.Insert(tag.Key)
desiredResourceTags = append(desiredResourceTags, tag)
}
}

var currentResourceTags []configv1.AWSResourceTag
if currentInfra.Status.PlatformStatus != nil &&
currentInfra.Status.PlatformStatus.AWS != nil {
currentResourceTags = currentInfra.Status.PlatformStatus.AWS.ResourceTags
}

if len(desiredResourceTags) == 0 && len(currentResourceTags) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like this case is handled by the deepequal on the next line. is it not?

return nil
}

if equality.Semantic.DeepEqual(desiredResourceTags, currentResourceTags) {
return nil
}

if currentInfra.Status.PlatformStatus == nil {
currentInfra.Status.PlatformStatus = &configv1.PlatformStatus{}
}
if currentInfra.Status.PlatformStatus.AWS == nil {
currentInfra.Status.PlatformStatus.AWS = &configv1.AWSPlatformStatus{}
}
currentInfra.Status.PlatformStatus.AWS.ResourceTags = desiredResourceTags
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless order matters, please sort the status resourcetags.


_, err = c.infraClient.UpdateStatus(ctx, currentInfra, metav1.UpdateOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an event on successful update is also warranted.

if err != nil {
syncCtx.Recorder().Warningf("AWSResourceTagsController", "Unable to update the infrastructure status")
return err
}
return nil
}

// validateTag checks the following things to ensure that the tag is acceptable as an additional tag.
// * The key and value contain only valid characters.
// * The key is not empty and at most 128 characters.
// * The value is not empty and at most 256 characters. Note that, while many AWS services accept empty tag values,
// the additional tags may be applied to resources in services that do not accept empty tag values. Consequently,
// OpenShift cannot accept empty tag values.
// * The key is not in the kubernetes.io namespace.
// * The key is not in the openshift.io namespace.
func validateTag(tag configv1.AWSResourceTag) error {
if !tagRegex.MatchString(tag.Key) {
return fmt.Errorf("key contains invalid characters")
}
if !tagRegex.MatchString(tag.Value) {
return fmt.Errorf("value contains invalid characters")
}
if len(tag.Key) == 0 {
return fmt.Errorf("key is empty")
}
if len(tag.Key) > 128 {
return fmt.Errorf("key is too long")
}
if len(tag.Value) == 0 {
return fmt.Errorf("value is empty")
}
if len(tag.Value) > 256 {
return fmt.Errorf("value is too long")
}
if kubernetesNamespaceRegex.MatchString(tag.Key) {
return fmt.Errorf("key is in the kubernetes.io namespace")
}
if openshiftNamespaceRegex.MatchString(tag.Key) {
return fmt.Errorf("key is in the openshift.io namespace")
}
return nil
}
Loading