Skip to content

Commit 080d04a

Browse files
committed
Add remote.ClusterClient to access remote workload clusters
Signed-off-by: Vince Prignano <[email protected]>
1 parent d48025d commit 080d04a

10 files changed

+509
-3
lines changed

pkg/controller/BUILD.bazel

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ go_library(
66
"add_machinedeployment.go",
77
"add_machineset.go",
88
"add_node.go",
9+
"add_noderef.go",
910
"controller.go",
1011
],
1112
importpath = "sigs.k8s.io/cluster-api/pkg/controller",
@@ -14,6 +15,7 @@ go_library(
1415
"//pkg/controller/machinedeployment:go_default_library",
1516
"//pkg/controller/machineset:go_default_library",
1617
"//pkg/controller/node:go_default_library",
18+
"//pkg/controller/noderef:go_default_library",
1719
"//vendor/sigs.k8s.io/controller-runtime/pkg/manager:go_default_library",
1820
],
1921
)

pkg/controller/add_noderef.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controller
18+
19+
import (
20+
"sigs.k8s.io/cluster-api/pkg/controller/noderef"
21+
)
22+
23+
func init() {
24+
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
25+
AddToManagerFuncs = append(AddToManagerFuncs, noderef.Add)
26+
}

pkg/controller/noderef/BUILD.bazel

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["noderef_controller.go"],
6+
importpath = "sigs.k8s.io/cluster-api/pkg/controller/noderef",
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//pkg/apis/cluster/v1alpha1:go_default_library",
10+
"//vendor/github.com/pkg/errors:go_default_library",
11+
"//vendor/k8s.io/api/core/v1:go_default_library",
12+
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
13+
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
14+
"//vendor/k8s.io/klog:go_default_library",
15+
"//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library",
16+
"//vendor/sigs.k8s.io/controller-runtime/pkg/controller:go_default_library",
17+
"//vendor/sigs.k8s.io/controller-runtime/pkg/handler:go_default_library",
18+
"//vendor/sigs.k8s.io/controller-runtime/pkg/manager:go_default_library",
19+
"//vendor/sigs.k8s.io/controller-runtime/pkg/reconcile:go_default_library",
20+
"//vendor/sigs.k8s.io/controller-runtime/pkg/source:go_default_library",
21+
],
22+
)
23+
24+
go_test(
25+
name = "go_default_test",
26+
srcs = [
27+
"noderef_controller_suite_test.go",
28+
"noderef_controller_test.go",
29+
],
30+
embed = [":go_default_library"],
31+
deps = [
32+
"//pkg/apis:go_default_library",
33+
"//pkg/apis/cluster/v1alpha1:go_default_library",
34+
"//vendor/github.com/onsi/gomega:go_default_library",
35+
"//vendor/golang.org/x/net/context:go_default_library",
36+
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
37+
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
38+
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
39+
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
40+
"//vendor/k8s.io/client-go/rest:go_default_library",
41+
"//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library",
42+
"//vendor/sigs.k8s.io/controller-runtime/pkg/envtest:go_default_library",
43+
"//vendor/sigs.k8s.io/controller-runtime/pkg/manager:go_default_library",
44+
"//vendor/sigs.k8s.io/controller-runtime/pkg/reconcile:go_default_library",
45+
],
46+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package noderef
18+
19+
import (
20+
"context"
21+
"encoding/base64"
22+
"fmt"
23+
24+
"github.com/pkg/errors"
25+
corev1 "k8s.io/api/core/v1"
26+
apierrors "k8s.io/apimachinery/pkg/api/errors"
27+
"k8s.io/apimachinery/pkg/runtime"
28+
"k8s.io/klog"
29+
v1alpha1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
30+
"sigs.k8s.io/controller-runtime/pkg/client"
31+
"sigs.k8s.io/controller-runtime/pkg/controller"
32+
"sigs.k8s.io/controller-runtime/pkg/handler"
33+
"sigs.k8s.io/controller-runtime/pkg/manager"
34+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
35+
"sigs.k8s.io/controller-runtime/pkg/source"
36+
)
37+
38+
// Add creates a new NodeRef Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
39+
// and Start it when the Manager is Started.
40+
func Add(mgr manager.Manager) error {
41+
return add(mgr, newReconciler(mgr))
42+
}
43+
44+
// newReconciler returns a new reconcile.Reconciler
45+
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
46+
return &ReconcileNodeRef{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
47+
}
48+
49+
// add adds a new Controller to mgr with r as the reconcile.Reconciler
50+
func add(mgr manager.Manager, r reconcile.Reconciler) error {
51+
// Create a new controller
52+
c, err := controller.New("noderef-controller", mgr, controller.Options{Reconciler: r})
53+
if err != nil {
54+
return err
55+
}
56+
57+
// Watch for changes to NodeRef
58+
err = c.Watch(&source.Kind{Type: &v1alpha1.Machine{}}, &handler.EnqueueRequestForObject{})
59+
if err != nil {
60+
return err
61+
}
62+
63+
return nil
64+
}
65+
66+
var _ reconcile.Reconciler = &ReconcileNodeRef{}
67+
68+
// ReconcileNodeRef reconciles a NodeRef object
69+
type ReconcileNodeRef struct {
70+
client.Client
71+
scheme *runtime.Scheme
72+
}
73+
74+
// Reconcile reads that state of the cluster for a Machine object and makes changes based on the state read
75+
// and what is in the Machine.Spec
76+
// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machines,verbs=get;list;watch;create;update;patch;delete
77+
// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machines/status,verbs=get;update;patch
78+
func (r *ReconcileNodeRef) Reconcile(request reconcile.Request) (reconcile.Result, error) {
79+
// Fetch the Machine instance
80+
machine := &v1alpha1.Machine{}
81+
err := r.Get(context.TODO(), request.NamespacedName, machine)
82+
if err != nil {
83+
if apierrors.IsNotFound(err) {
84+
return reconcile.Result{}, nil
85+
}
86+
return reconcile.Result{}, err
87+
}
88+
89+
if machine.Status.NodeRef != nil {
90+
return reconcile.Result{}, nil
91+
}
92+
93+
clusterName, ok := machine.Labels[v1alpha1.MachineClusterLabelName]
94+
if !ok {
95+
klog.Warningf("Cannot set noderef for Machine %q: missing cluster label", machine.Name)
96+
return reconcile.Result{}, nil
97+
}
98+
99+
secret := &corev1.Secret{}
100+
secretKey := client.ObjectKey{
101+
Namespace: machine.Namespace,
102+
Name: fmt.Sprintf("%s-kubeconfig", clusterName),
103+
}
104+
if err := r.Get(context.TODO(), secretKey, secret); err != nil {
105+
if apierrors.IsNotFound(err) {
106+
klog.Warningf("Cannot set noderef for Machine %q, required secret %v not found", machine.Name, secretKey)
107+
return reconcile.Result{}, nil
108+
}
109+
return reconcile.Result{}, err
110+
}
111+
112+
encodedKubeconfig, ok := secret.Data["value"]
113+
if !ok {
114+
klog.Warningf("Cannot set noderef for Machine %q: missing value in secret %q", machine.Name, secretKey)
115+
return reconcile.Result{}, nil
116+
}
117+
118+
_, err = base64.StdEncoding.DecodeString(string(encodedKubeconfig))
119+
if err != nil {
120+
return reconcile.Result{}, errors.Wrapf(err, "Cannot decode kubeconfig secret %q", secretKey)
121+
}
122+
123+
return reconcile.Result{}, nil
124+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package noderef
18+
19+
import (
20+
stdlog "log"
21+
"os"
22+
"path/filepath"
23+
"sync"
24+
"testing"
25+
26+
"github.com/onsi/gomega"
27+
"k8s.io/client-go/kubernetes/scheme"
28+
"k8s.io/client-go/rest"
29+
"sigs.k8s.io/cluster-api/pkg/apis"
30+
"sigs.k8s.io/controller-runtime/pkg/envtest"
31+
"sigs.k8s.io/controller-runtime/pkg/manager"
32+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
33+
)
34+
35+
var cfg *rest.Config
36+
37+
func TestMain(m *testing.M) {
38+
t := &envtest.Environment{
39+
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
40+
}
41+
apis.AddToScheme(scheme.Scheme)
42+
43+
var err error
44+
if cfg, err = t.Start(); err != nil {
45+
stdlog.Fatal(err)
46+
}
47+
48+
code := m.Run()
49+
t.Stop()
50+
os.Exit(code)
51+
}
52+
53+
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
54+
// writes the request to requests after Reconcile is finished.
55+
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
56+
requests := make(chan reconcile.Request)
57+
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
58+
result, err := inner.Reconcile(req)
59+
requests <- req
60+
return result, err
61+
})
62+
return fn, requests
63+
}
64+
65+
// StartTestManager adds recFn
66+
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
67+
stop := make(chan struct{})
68+
wg := &sync.WaitGroup{}
69+
wg.Add(1)
70+
go func() {
71+
defer wg.Done()
72+
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
73+
}()
74+
return stop, wg
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package noderef
18+
19+
import (
20+
"testing"
21+
"time"
22+
23+
"github.com/onsi/gomega"
24+
"golang.org/x/net/context"
25+
apierrors "k8s.io/apimachinery/pkg/api/errors"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/apimachinery/pkg/types"
28+
v1alpha1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
"sigs.k8s.io/controller-runtime/pkg/manager"
31+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
32+
)
33+
34+
var c client.Client
35+
36+
var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}}
37+
38+
const timeout = time.Second * 5
39+
40+
func TestReconcile(t *testing.T) {
41+
g := gomega.NewGomegaWithT(t)
42+
instance := &v1alpha1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}}
43+
44+
// Setup the Manager and Controller. Wrap the Controller Reconcile function so it writes each request to a
45+
// channel when it is finished.
46+
mgr, err := manager.New(cfg, manager.Options{})
47+
g.Expect(err).NotTo(gomega.HaveOccurred())
48+
c = mgr.GetClient()
49+
50+
recFn, requests := SetupTestReconcile(newReconciler(mgr))
51+
g.Expect(add(mgr, recFn)).NotTo(gomega.HaveOccurred())
52+
53+
stopMgr, mgrStopped := StartTestManager(mgr, g)
54+
55+
defer func() {
56+
close(stopMgr)
57+
mgrStopped.Wait()
58+
}()
59+
60+
// Create the NodeRef object and expect the Reconcile
61+
err = c.Create(context.TODO(), instance)
62+
// The instance object may not be a valid object because it might be missing some required fields.
63+
// Please modify the instance object by adding required fields and then remove the following if statement.
64+
if apierrors.IsInvalid(err) {
65+
t.Logf("failed to create object, got an invalid object error: %v", err)
66+
return
67+
}
68+
g.Expect(err).NotTo(gomega.HaveOccurred())
69+
defer c.Delete(context.TODO(), instance)
70+
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
71+
72+
}

0 commit comments

Comments
 (0)