Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.

Commit 9398a8f

Browse files
authored
Move clientset implementation to use dynamic client and add a fake + tests (#69)
* Use dynamic client in clientset and add fake for tests * Fix copyright * Fix imports * Update go mod and go version * Update github workflows for go 1.19 * Update kind version * Use kind install * go install kustomize * Update kustomize
1 parent db15825 commit 9398a8f

File tree

13 files changed

+729
-281
lines changed

13 files changed

+729
-281
lines changed

.github/workflows/ci-grpc.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ jobs:
1010
test:
1111

1212
runs-on: ubuntu-latest
13-
13+
1414
steps:
1515
- uses: actions/setup-go@v2
1616
with:
17-
go-version: 1.17.x
17+
go-version: 1.19.x
1818
- uses: actions/checkout@v2
1919
- name: install-deps
20-
run: sudo apt-get install libpcap-dev
20+
run: sudo apt-get install libpcap-dev
2121
- name: run go test
2222
run: make test
2323

@@ -27,7 +27,7 @@ jobs:
2727
steps:
2828
- uses: actions/setup-go@v2
2929
with:
30-
go-version: 1.17.x
30+
go-version: 1.19.x
3131
- uses: actions/checkout@v2
3232

3333
- name: Set up Docker Buildx

.github/workflows/ci.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ jobs:
1010
test:
1111

1212
runs-on: ubuntu-latest
13-
13+
1414
steps:
1515
- uses: actions/setup-go@v2
1616
with:
17-
go-version: 1.17.x
17+
go-version: 1.19.x
1818
- uses: actions/checkout@v2
19-
19+
2020
- name: install-deps
21-
run: sudo apt-get install libpcap-dev
21+
run: sudo apt-get install libpcap-dev
2222
- name: run go test
2323
run: make test
2424

@@ -27,7 +27,7 @@ jobs:
2727
steps:
2828
- uses: actions/setup-go@v2
2929
with:
30-
go-version: 1.17.x
30+
go-version: 1.19.x
3131
- uses: actions/checkout@v2
3232

3333
- name: Set up Docker Buildx

.github/workflows/release.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ jobs:
1414
uses: actions/checkout@v2
1515
with:
1616
fetch-depth: 0
17-
-
17+
-
1818
name: Set up Docker Buildx
1919
uses: docker/setup-buildx-action@v1
20-
-
20+
-
2121
name: Login to Container Registry
2222
uses: docker/login-action@v1
2323
with:
2424
registry: docker.io
2525
username: ${{ secrets.DOCKER_USER }}
2626
password: ${{ secrets.DOCKER_PWD }}
27-
-
27+
-
2828
name: Build and Push container images
2929
run: |
30-
make release
30+
make release

.mk/kind.mk

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22
KIND_CLUSTER_NAME := "meshnet"
33

44
.PHONY: kind-install
5-
kind-install:
6-
go get sigs.k8s.io/kind@v0.11.1
5+
kind-install:
6+
go install sigs.k8s.io/kind@v0.17.0
77

88
.PHONY: kind-stop
9-
kind-stop:
9+
kind-stop:
1010
@$(GOPATH)/bin/kind delete cluster --name $(KIND_CLUSTER_NAME) || \
1111
echo "kind cluster is not running"
1212

13-
.PHONY: kind-ensure
14-
kind-ensure:
13+
.PHONY: kind-ensure
14+
kind-ensure:
1515
@which $(GOPATH)/bin/kind >/dev/null 2>&1 || \
1616
make kind-install
1717

1818
.PHONY: kind-start
19-
kind-start: kind-ensure
19+
kind-start: kind-ensure
2020
@$(GOPATH)/bin/kind get clusters | grep $(KIND_CLUSTER_NAME) >/dev/null 2>&1 || \
21-
$(GOPATH)/bin/kind create cluster --name $(KIND_CLUSTER_NAME) --config ./kind.yaml
21+
$(GOPATH)/bin/kind create cluster --name $(KIND_CLUSTER_NAME) --config ./kind.yaml
2222

2323
.PHONY: kind-wait-for-cni
2424
kind-wait-for-cni:
@@ -29,5 +29,5 @@ kind-connect:
2929
kubectl cluster-info --context kind-meshnet >/dev/null
3030

3131
.PHONY: kind-load
32-
kind-load:
32+
kind-load:
3333
$(GOPATH)/bin/kind load docker-image --name $(KIND_CLUSTER_NAME) ${DOCKER_IMAGE}:${COMMIT}

.mk/kustomize.mk

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
.PHONY: kust-install
2-
kust-install:
3-
go get sigs.k8s.io/kustomize/kustomize/v3
2+
kust-install:
3+
go install sigs.k8s.io/kustomize/kustomize/v5@v5.0.0
44

5-
.PHONY: kust-ensure
6-
kust-ensure:
5+
.PHONY: kust-ensure
6+
kust-ensure:
77
@which $(GOPATH)/bin/kustomize >/dev/null 2>&1 || \
88
make kust-install
99

@@ -15,5 +15,5 @@ kustomize: kust-ensure
1515

1616

1717
.PHONY: kustomize-kops
18-
kustomize-kops: kust-ensure
19-
kubectl apply -k manifests/overlays/kops/
18+
kustomize-kops: kust-ensure
19+
kubectl apply -k manifests/overlays/kops/

api/clientset/v1beta1/fake/fake.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package fake
15+
16+
import (
17+
toplogyv1client "github.com/networkop/meshnet-cni/api/clientset/v1beta1"
18+
topologyv1 "github.com/networkop/meshnet-cni/api/types/v1beta1"
19+
"k8s.io/apimachinery/pkg/runtime"
20+
dfake "k8s.io/client-go/dynamic/fake"
21+
"k8s.io/client-go/rest"
22+
)
23+
24+
func NewSimpleClientset(objects ...runtime.Object) (*toplogyv1client.Clientset, error) {
25+
cs, err := toplogyv1client.NewForConfig(&rest.Config{})
26+
if err != nil {
27+
return nil, err
28+
}
29+
c := dfake.NewSimpleDynamicClient(topologyv1.Scheme, objects...)
30+
cs.SetDynamicClient(c.Resource(toplogyv1client.GVR()))
31+
return cs, nil
32+
}

api/clientset/v1beta1/topology.go

Lines changed: 69 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@ import (
2626
"k8s.io/client-go/dynamic"
2727
"k8s.io/client-go/kubernetes/scheme"
2828
"k8s.io/client-go/rest"
29+
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
2930

3031
topologyv1 "github.com/networkop/meshnet-cni/api/types/v1beta1"
3132
)
3233

3334
// TopologyInterface provides access to the Topology CRD.
3435
type TopologyInterface interface {
3536
List(ctx context.Context, opts metav1.ListOptions) (*topologyv1.TopologyList, error)
36-
Get(ctx context.Context, name string, options metav1.GetOptions) (*topologyv1.Topology, error)
37-
Create(ctx context.Context, topology *topologyv1.Topology) (*topologyv1.Topology, error)
37+
Get(ctx context.Context, name string, opts metav1.GetOptions) (*topologyv1.Topology, error)
38+
Create(ctx context.Context, topology *topologyv1.Topology, opts metav1.CreateOptions) (*topologyv1.Topology, error)
3839
Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
3940
Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
4041
Unstructured(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error)
@@ -49,19 +50,33 @@ type Interface interface {
4950
// Clientset is a client for the topology crds.
5051
type Clientset struct {
5152
dInterface dynamic.NamespaceableResourceInterface
52-
restClient rest.Interface
5353
}
5454

5555
var gvr = schema.GroupVersionResource{
56-
Group: "networkop.co.uk",
57-
Version: "v1beta1",
56+
Group: topologyv1.GroupName,
57+
Version: topologyv1.GroupVersion,
5858
Resource: "topologies",
5959
}
6060

61+
func GVR() schema.GroupVersionResource {
62+
return gvr
63+
}
64+
65+
var (
66+
groupVersion = &schema.GroupVersion{
67+
Group: topologyv1.GroupName,
68+
Version: topologyv1.GroupVersion,
69+
}
70+
)
71+
72+
func GV() *schema.GroupVersion {
73+
return groupVersion
74+
}
75+
6176
// NewForConfig returns a new Clientset based on c.
6277
func NewForConfig(c *rest.Config) (*Clientset, error) {
6378
config := *c
64-
config.ContentConfig.GroupVersion = &schema.GroupVersion{Group: topologyv1.GroupName, Version: topologyv1.GroupVersion}
79+
config.ContentConfig.GroupVersion = groupVersion
6580
config.APIPath = "/apis"
6681
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
6782
config.UserAgent = rest.DefaultKubernetesUserAgent()
@@ -70,108 +85,95 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
7085
return nil, err
7186
}
7287
dInterface := dClient.Resource(gvr)
73-
rClient, err := rest.RESTClientFor(&config)
74-
if err != nil {
75-
return nil, err
76-
}
77-
return &Clientset{
78-
dInterface: dInterface,
79-
restClient: rClient,
80-
}, nil
88+
return &Clientset{dInterface: dInterface}, nil
89+
}
90+
91+
// SetDynamicClient is only exposed for integration testing.
92+
func (c *Clientset) SetDynamicClient(d dynamic.NamespaceableResourceInterface) {
93+
c.dInterface = d
8194
}
8295

8396
func (c *Clientset) Topology(namespace string) TopologyInterface {
8497
return &topologyClient{
8598
dInterface: c.dInterface,
86-
restClient: c.restClient,
8799
ns: namespace,
88100
}
89101
}
90102

91103
type topologyClient struct {
92104
dInterface dynamic.NamespaceableResourceInterface
93-
restClient rest.Interface
94105
ns string
95106
}
96107

97108
func (t *topologyClient) List(ctx context.Context, opts metav1.ListOptions) (*topologyv1.TopologyList, error) {
109+
u, err := t.dInterface.Namespace(t.ns).List(ctx, opts)
110+
if err != nil {
111+
return nil, err
112+
}
98113
result := topologyv1.TopologyList{}
99-
err := t.restClient.
100-
Get().
101-
Namespace(t.ns).
102-
Resource("topologies").
103-
VersionedParams(&opts, scheme.ParameterCodec).
104-
Do(ctx).
105-
Into(&result)
106-
107-
return &result, err
114+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &result); err != nil {
115+
return nil, fmt.Errorf("failed to type assert return to TopologyList: %w", err)
116+
}
117+
return &result, nil
108118
}
109119

110120
func (t *topologyClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*topologyv1.Topology, error) {
121+
u, err := t.dInterface.Namespace(t.ns).Get(ctx, name, opts)
122+
if err != nil {
123+
return nil, err
124+
}
111125
result := topologyv1.Topology{}
112-
err := t.restClient.
113-
Get().
114-
Namespace(t.ns).
115-
Resource("topologies").
116-
Name(name).
117-
VersionedParams(&opts, scheme.ParameterCodec).
118-
Do(ctx).
119-
Into(&result)
120-
121-
return &result, err
126+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &result); err != nil {
127+
return nil, fmt.Errorf("failed to type assert return to Topology: %w", err)
128+
}
129+
return &result, nil
122130
}
123131

124-
func (t *topologyClient) Create(ctx context.Context, topology *topologyv1.Topology) (*topologyv1.Topology, error) {
132+
func (t *topologyClient) Create(ctx context.Context, topology *topologyv1.Topology, opts metav1.CreateOptions) (*topologyv1.Topology, error) {
133+
gvk, err := apiutil.GVKForObject(topology, topologyv1.Scheme)
134+
if err != nil {
135+
return nil, fmt.Errorf("failed to get gvk for Topology: %w", err)
136+
}
137+
topology.TypeMeta = metav1.TypeMeta{
138+
Kind: gvk.Kind,
139+
APIVersion: gvk.GroupVersion().String(),
140+
}
141+
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(topology)
142+
if err != nil {
143+
return nil, fmt.Errorf("failed to convert Topology to unstructured: %w", err)
144+
}
145+
u, err := t.dInterface.Namespace(t.ns).Create(ctx, &unstructured.Unstructured{Object: obj}, opts)
146+
if err != nil {
147+
return nil, err
148+
}
125149
result := topologyv1.Topology{}
126-
err := t.restClient.
127-
Post().
128-
Namespace(t.ns).
129-
Resource("topologies").
130-
Body(topology).
131-
Do(ctx).
132-
Into(&result)
133-
134-
return &result, err
150+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &result); err != nil {
151+
return nil, fmt.Errorf("failed to type assert return to Topology: %w", err)
152+
}
153+
return &result, nil
135154
}
136155

137156
func (t *topologyClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
138157
opts.Watch = true
139-
return t.restClient.
140-
Get().
141-
Namespace(t.ns).
142-
Resource("topologies").
143-
VersionedParams(&opts, scheme.ParameterCodec).
144-
Watch(ctx)
158+
return t.dInterface.Namespace(t.ns).Watch(ctx, opts)
145159
}
146160

147161
func (t *topologyClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
148-
return t.restClient.
149-
Delete().
150-
Namespace(t.ns).
151-
Resource("topologies").
152-
VersionedParams(&opts, scheme.ParameterCodec).
153-
Name(name).
154-
Do(ctx).
155-
Error()
162+
return t.dInterface.Namespace(t.ns).Delete(ctx, name, opts)
156163
}
157164

158165
func (t *topologyClient) Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*topologyv1.Topology, error) {
159-
result := topologyv1.Topology{}
160166
obj, err := t.dInterface.Namespace(t.ns).UpdateStatus(ctx, obj, metav1.UpdateOptions{})
161167
if err != nil {
162168
return nil, err
163169
}
164-
err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &result)
165-
if err != nil {
166-
return nil, fmt.Errorf("failed to type assert return to topology")
170+
result := topologyv1.Topology{}
171+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &result); err != nil {
172+
return nil, fmt.Errorf("failed to type assert return to Topology: %w", err)
167173
}
168174
return &result, nil
169175
}
170176

171177
func (t *topologyClient) Unstructured(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
172178
return t.dInterface.Namespace(t.ns).Get(ctx, name, opts, subresources...)
173179
}
174-
175-
func init() {
176-
topologyv1.AddToScheme(scheme.Scheme)
177-
}

0 commit comments

Comments
 (0)