Skip to content

Commit

Permalink
rollouts: add package cluster matcher (kptdev#3700)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherFry authored and droot committed Jan 24, 2023
1 parent c07d23c commit 9f66602
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 9 deletions.
13 changes: 12 additions & 1 deletion rollouts/api/v1alpha1/rollout_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type RolloutSpec struct {

// Targets specifies the clusters that will receive the KRM config packages.
Targets ClusterTargetSelector `json:"targets,omitempty"`

// PackageToTargetMatcher specifies the clusters that will receive a specific package.
PackageToTargetMatcher PackageToClusterMatcher `json:"packageToTargetMatcher"`
}

type ClusterTargetSelector struct {
Expand Down Expand Up @@ -65,7 +68,7 @@ type GitSource struct {
// GitSelector defines the selector to apply to Git.
type GitSelector struct {
Org string `json:"org"`
Name string `json:"name"`
Repo string `json:"repo"`
Directory string `json:"directory"`
Revision string `json:"revision"`
SecretRef SecretReference `json:"secretRef,omitempty"`
Expand All @@ -77,6 +80,14 @@ type SecretReference struct {
Name string `json:"name,omitempty"`
}

// +kubebuilder:validation:Enum=CEL
type MatcherType string

type PackageToClusterMatcher struct {
Type MatcherType `json:"type"`
MatchExpression string `json:"matchExpression"`
}

// RolloutStatus defines the observed state of Rollout
type RolloutStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
Expand Down
16 changes: 16 additions & 0 deletions rollouts/api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 18 additions & 3 deletions rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ spec:
description:
description: Description is a user friendly description of this Rollout.
type: string
packageToTargetMatcher:
description: PackageToTargetMatcher specifies the clusters that will
receive a specific package.
properties:
matchExpression:
type: string
type:
enum:
- CEL
type: string
required:
- matchExpression
- type
type: object
packages:
description: Packages source for this Rollout.
properties:
Expand All @@ -50,10 +64,10 @@ spec:
properties:
directory:
type: string
name:
type: string
org:
type: string
repo:
type: string
revision:
type: string
secretRef:
Expand All @@ -66,8 +80,8 @@ spec:
type: object
required:
- directory
- name
- org
- repo
- revision
type: object
required:
Expand Down Expand Up @@ -135,6 +149,7 @@ spec:
x-kubernetes-map-type: atomic
type: object
required:
- packageToTargetMatcher
- packages
type: object
status:
Expand Down
27 changes: 27 additions & 0 deletions rollouts/config/samples/container_cluster_maui.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: container.cnrm.cloud.google.com/v1beta1
kind: ContainerCluster
metadata:
name: containercluster-maui
namespace: config-control
labels:
location/island: maui
spec:
description: Maui autopilot cluster
enableAutopilot: true
location: us-central1
releaseChannel:
channel: REGULAR
27 changes: 27 additions & 0 deletions rollouts/config/samples/container_cluster_oahu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: container.cnrm.cloud.google.com/v1beta1
kind: ContainerCluster
metadata:
name: containercluster-oahu
namespace: config-control
labels:
location/island: oahu
spec:
description: Oahu autopilot cluster
enableAutopilot: true
location: us-central1
releaseChannel:
channel: REGULAR
5 changes: 4 additions & 1 deletion rollouts/config/samples/gitops_v1alpha1_rollout.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ spec:
git:
selector:
org: GoogleContainerTools
name: kpt-samples
repo: kpt-samples
directory: "*"
revision: main
targets:
selector:
matchExpressions:
- {key: location/island, operator: In, values: [oahu, maui]}
packageToTargetMatcher:
type: CEL
matchExpression: 'true'
16 changes: 15 additions & 1 deletion rollouts/controllers/rollout_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1"
gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1"
"github.com/GoogleContainerTools/kpt/rollouts/pkg/clusterstore"
"github.com/GoogleContainerTools/kpt/rollouts/pkg/packageclustermatcher"
"github.com/GoogleContainerTools/kpt/rollouts/pkg/packagediscovery"
)

Expand Down Expand Up @@ -109,7 +110,20 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
return ctrl.Result{}, client.IgnoreNotFound(err)
}

logger.Info("discovered packages", "count", len(discoveredPackages), "packages", discoveredPackages)
packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(gkeClusters.Items, discoveredPackages)

allClusterPackages, err := packageClusterMatcherClient.GetClusterPackages(rollout.Spec.PackageToTargetMatcher)

if err != nil {
logger.Error(err, "get cluster packages failed")
return ctrl.Result{}, client.IgnoreNotFound(err)
}

for _, clusterPackages := range allClusterPackages {
clusterName := clusterPackages.Cluster.Name

logger.Info("cluster packages", "cluster", clusterName, "packagesCount", len(clusterPackages.Packages), "packages", clusterPackages.Packages)
}

return ctrl.Result{}, err
}
Expand Down
3 changes: 3 additions & 0 deletions rollouts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
cloud.google.com/go/iam v0.7.0
github.com/GoogleCloudPlatform/k8s-config-connector v1.98.0
github.com/golang/protobuf v1.5.2
github.com/google/cel-go v0.13.0
github.com/google/go-github/v48 v48.2.0
github.com/onsi/ginkgo/v2 v2.1.6
github.com/onsi/gomega v1.20.1
Expand All @@ -28,6 +29,7 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down Expand Up @@ -63,6 +65,7 @@ require (
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions rollouts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
Expand Down Expand Up @@ -169,6 +171,8 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU=
github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpDgIhJ8s=
github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
Expand Down Expand Up @@ -303,6 +307,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
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/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
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=
Expand Down
118 changes: 118 additions & 0 deletions rollouts/pkg/packageclustermatcher/packageclustermatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package packageclustermatcher

import (
"fmt"

gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1"
gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1"
"github.com/GoogleContainerTools/kpt/rollouts/pkg/packagediscovery"

"github.com/google/cel-go/cel"
"github.com/google/cel-go/checker/decls"
)

type PackageClusterMatcher struct {
clusters []gkeclusterapis.ContainerCluster
packages []packagediscovery.DiscoveredPackage
}

type ClusterPackages struct {
Cluster gkeclusterapis.ContainerCluster
Packages []packagediscovery.DiscoveredPackage
}

func NewPackageClusterMatcher(clusters []gkeclusterapis.ContainerCluster, packages []packagediscovery.DiscoveredPackage) *PackageClusterMatcher {
return &PackageClusterMatcher{
clusters: clusters,
packages: packages,
}
}

func (m *PackageClusterMatcher) GetClusterPackages(matcher gitopsv1alpha1.PackageToClusterMatcher) ([]ClusterPackages, error) {
clusters := m.clusters
packages := m.packages

allClusterPackages := []ClusterPackages{}

for _, cluster := range clusters {
matchedPackages := []packagediscovery.DiscoveredPackage{}

celCluster := map[string]interface{}{
"name": cluster.ObjectMeta.Name,
"labels": cluster.ObjectMeta.Labels,
}

for _, discoveredPackage := range packages {
celPackage := map[string]interface{}{
"org": discoveredPackage.Org,
"repo": discoveredPackage.Repo,
"directory": discoveredPackage.Directory,
}

isMatch, err := isPackageClusterMatch(matcher.MatchExpression, celCluster, celPackage)
if err != nil {
return nil, fmt.Errorf("unable to execute package cluster matcher expression: %w", err)
}

if isMatch {
matchedPackages = append(matchedPackages, discoveredPackage)
}
}

clusterPackages := ClusterPackages{
Cluster: cluster,
Packages: matchedPackages,
}

allClusterPackages = append(allClusterPackages, clusterPackages)
}

return allClusterPackages, nil
}

func isPackageClusterMatch(expr string, cluster, rolloutPackage map[string]interface{}) (bool, error) {
env, err := cel.NewEnv(
cel.Declarations(
decls.NewVar("cluster", decls.Dyn),
decls.NewVar("rolloutPackage", decls.Dyn),
))
if err != nil {
return false, err
}

p, issue := env.Parse(expr)
if issue != nil && issue.Err() != nil {
return false, issue.Err()
}

c, issue := env.Check(p)
if issue != nil && issue.Err() != nil {
return false, issue.Err()
}

prg, err := env.Program(c)
if err != nil {
return false, err
}

out, _, err := prg.Eval(map[string]interface{}{
"cluster": cluster,
"rolloutPackage": rolloutPackage,
})

return out.Value().(bool), err
}
Loading

0 comments on commit 9f66602

Please sign in to comment.