Skip to content

Commit 8178db9

Browse files
committed
Start verifying that existing provider contracts are up to date with docs
1 parent 77b011e commit 8178db9

9 files changed

+694
-7
lines changed

internal/contract/bootstrap.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
Copyright 2022 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 contract
18+
19+
import "sync"
20+
21+
// BootstrapContract encodes information about the Cluster API contract for bootstrap objects.
22+
type BootstrapContract struct{}
23+
24+
var bootstrap *BootstrapContract
25+
var onceBootstrap sync.Once
26+
27+
// Bootstrap provide access to the information about the Cluster API contract for bootstrap objects.
28+
func Bootstrap() *BootstrapContract {
29+
onceBootstrap.Do(func() {
30+
bootstrap = &BootstrapContract{}
31+
})
32+
return bootstrap
33+
}
34+
35+
// Ready provide access to status.ready field in a bootstrap object.
36+
func (b *BootstrapContract) Ready() *Bool {
37+
return &Bool{
38+
path: []string{"status", "ready"},
39+
}
40+
}
41+
42+
// DataSecretName provide access to status.dataSecretName field in a bootstrap object.
43+
func (b *BootstrapContract) DataSecretName() *String {
44+
return &String{
45+
path: []string{"status", "dataSecretName"},
46+
}
47+
}
48+
49+
// FailureReason provides access to the status.failureReason field in an bootstrap object. Note that this field is optional.
50+
func (b *BootstrapContract) FailureReason() *String {
51+
return &String{
52+
path: []string{"status", "failureReason"},
53+
}
54+
}
55+
56+
// FailureMessage provides access to the status.failureMessage field in an bootstrap object. Note that this field is optional.
57+
func (b *BootstrapContract) FailureMessage() *String {
58+
return &String{
59+
path: []string{"status", "failureMessage"},
60+
}
61+
}

internal/contract/bootstrap_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
Copyright 2022 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 contract
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/gomega"
23+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24+
)
25+
26+
func TestBootstrap(t *testing.T) {
27+
obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
28+
29+
t.Run("Manages status.ready", func(t *testing.T) {
30+
g := NewWithT(t)
31+
32+
g.Expect(Bootstrap().Ready().Path()).To(Equal(Path{"status", "ready"}))
33+
34+
err := Bootstrap().Ready().Set(obj, true)
35+
g.Expect(err).ToNot(HaveOccurred())
36+
37+
got, err := Bootstrap().Ready().Get(obj)
38+
g.Expect(err).ToNot(HaveOccurred())
39+
g.Expect(got).ToNot(BeNil())
40+
g.Expect(*got).To(Equal(true))
41+
})
42+
t.Run("Manages status.dataSecretName", func(t *testing.T) {
43+
g := NewWithT(t)
44+
45+
g.Expect(Bootstrap().DataSecretName().Path()).To(Equal(Path{"status", "dataSecretName"}))
46+
47+
err := Bootstrap().DataSecretName().Set(obj, "fake-data-secret-name")
48+
g.Expect(err).ToNot(HaveOccurred())
49+
50+
got, err := Bootstrap().DataSecretName().Get(obj)
51+
g.Expect(err).ToNot(HaveOccurred())
52+
g.Expect(got).ToNot(BeNil())
53+
g.Expect(*got).To(Equal("fake-data-secret-name"))
54+
})
55+
t.Run("Manages optional status.failureReason", func(t *testing.T) {
56+
g := NewWithT(t)
57+
58+
g.Expect(Bootstrap().FailureReason().Path()).To(Equal(Path{"status", "failureReason"}))
59+
60+
err := Bootstrap().FailureReason().Set(obj, "fake-reason")
61+
g.Expect(err).ToNot(HaveOccurred())
62+
63+
got, err := Bootstrap().FailureReason().Get(obj)
64+
g.Expect(err).ToNot(HaveOccurred())
65+
g.Expect(got).ToNot(BeNil())
66+
g.Expect(*got).To(Equal("fake-reason"))
67+
})
68+
t.Run("Manages optional status.failureMessage", func(t *testing.T) {
69+
g := NewWithT(t)
70+
71+
g.Expect(Bootstrap().FailureMessage().Path()).To(Equal(Path{"status", "failureMessage"}))
72+
73+
err := Bootstrap().FailureMessage().Set(obj, "fake-message")
74+
g.Expect(err).ToNot(HaveOccurred())
75+
76+
got, err := Bootstrap().FailureMessage().Get(obj)
77+
g.Expect(err).ToNot(HaveOccurred())
78+
g.Expect(got).ToNot(BeNil())
79+
g.Expect(*got).To(Equal("fake-message"))
80+
})
81+
}

internal/contract/controlplane.go

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (c *ControlPlaneContract) MachineTemplate() *ControlPlaneMachineTemplate {
4949
return &ControlPlaneMachineTemplate{}
5050
}
5151

52-
// Version provide access to version field in a ControlPlane object, if any.
52+
// Version provide access to version field in a ControlPlane object, if any.
5353
// NOTE: When working with unstructured there is no way to understand if the ControlPlane provider
5454
// do support a field in the type definition from the fact that a field is not set in a given instance.
5555
// This is why in we are deriving if version is required from the ClusterClass in the topology reconciler code.
@@ -59,14 +59,28 @@ func (c *ControlPlaneContract) Version() *String {
5959
}
6060
}
6161

62-
// StatusVersion provide access to version field in a ControlPlane object status, if any.
62+
// StatusVersion provide access to the version field in a ControlPlane object status, if any.
6363
func (c *ControlPlaneContract) StatusVersion() *String {
6464
return &String{
6565
path: []string{"status", "version"},
6666
}
6767
}
6868

69-
// Replicas provide access to replicas field in a ControlPlane object, if any.
69+
// Ready provide access to the status.ready field in a ControlPlane object.
70+
func (c *ControlPlaneContract) Ready() *Bool {
71+
return &Bool{
72+
path: []string{"status", "ready"},
73+
}
74+
}
75+
76+
// Initialized provide access to status.initialized field in a ControlPlane object.
77+
func (c *ControlPlaneContract) Initialized() *Bool {
78+
return &Bool{
79+
path: []string{"status", "initialized"},
80+
}
81+
}
82+
83+
// Replicas provide access to replicas field in a ControlPlane object, if any.
7084
// NOTE: When working with unstructured there is no way to understand if the ControlPlane provider
7185
// do support a field in the type definition from the fact that a field is not set in a given instance.
7286
// This is why in we are deriving if replicas is required from the ClusterClass in the topology reconciler code.
@@ -76,27 +90,63 @@ func (c *ControlPlaneContract) Replicas() *Int64 {
7690
}
7791
}
7892

79-
// StatusReplicas provide access to status.replicas field in a ControlPlane object, if any.
93+
// StatusReplicas provide access to the status.replicas field in a ControlPlane object, if any. Applies to implementations using replicas.
8094
func (c *ControlPlaneContract) StatusReplicas() *Int64 {
8195
return &Int64{
8296
path: []string{"status", "replicas"},
8397
}
8498
}
8599

86-
// UpdatedReplicas provide access to status.updatedReplicas field in a ControlPlane object, if any.
100+
// UpdatedReplicas provide access to the status.updatedReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
87101
func (c *ControlPlaneContract) UpdatedReplicas() *Int64 {
88102
return &Int64{
89103
path: []string{"status", "updatedReplicas"},
90104
}
91105
}
92106

93-
// ReadyReplicas provide access to status.readyReplicas field in a ControlPlane object, if any.
107+
// ReadyReplicas provide access to the status.readyReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
94108
func (c *ControlPlaneContract) ReadyReplicas() *Int64 {
95109
return &Int64{
96110
path: []string{"status", "readyReplicas"},
97111
}
98112
}
99113

114+
// UnavailableReplicas provide access to the status.unavailableReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
115+
func (c *ControlPlaneContract) UnavailableReplicas() *Int64 {
116+
return &Int64{
117+
path: []string{"status", "unavailableReplicas"},
118+
}
119+
}
120+
121+
// Selector provide access to the status.selector field in a ControlPlane object, if any. Applies to implementations using replicas.
122+
func (c *ControlPlaneContract) Selector() *String {
123+
return &String{
124+
path: []string{"status", "selector"},
125+
}
126+
}
127+
128+
// FailureReason provides access to the status.failureReason field in an ControlPlane object. Note that this field is optional.
129+
func (c *ControlPlaneContract) FailureReason() *String {
130+
return &String{
131+
path: []string{"status", "failureReason"},
132+
}
133+
}
134+
135+
// FailureMessage provides access to the status.failureMessage field in an ControlPlane object. Note that this field is optional.
136+
func (c *ControlPlaneContract) FailureMessage() *String {
137+
return &String{
138+
path: []string{"status", "failureMessage"},
139+
}
140+
}
141+
142+
// ExternalManagedControlPlane provides access to the status.externalManagedControlPlane field in an ControlPlane object.
143+
// Note that this field is optional.
144+
func (c *ControlPlaneContract) ExternalManagedControlPlane() *Bool {
145+
return &Bool{
146+
path: []string{"status", "externalManagedControlPlane"},
147+
}
148+
}
149+
100150
// IsProvisioning returns true if the control plane is being created for the first time.
101151
// Returns false, if the control plane was already previously provisioned.
102152
func (c *ControlPlaneContract) IsProvisioning(obj *unstructured.Unstructured) (bool, error) {

internal/contract/controlplane_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,32 @@ func TestControlPlane(t *testing.T) {
5656
g.Expect(got).ToNot(BeNil())
5757
g.Expect(*got).To(Equal("1.2.3"))
5858
})
59+
t.Run("Manages status.ready", func(t *testing.T) {
60+
g := NewWithT(t)
61+
62+
g.Expect(ControlPlane().Ready().Path()).To(Equal(Path{"status", "ready"}))
63+
64+
err := ControlPlane().Ready().Set(obj, true)
65+
g.Expect(err).NotTo(HaveOccurred())
66+
67+
got, err := ControlPlane().Ready().Get(obj)
68+
g.Expect(err).ToNot(HaveOccurred())
69+
g.Expect(got).ToNot(BeNil())
70+
g.Expect(*got).To(Equal(true))
71+
})
72+
t.Run("Manages status.initialized", func(t *testing.T) {
73+
g := NewWithT(t)
74+
75+
g.Expect(ControlPlane().Initialized().Path()).To(Equal(Path{"status", "initialized"}))
76+
77+
err := ControlPlane().Initialized().Set(obj, true)
78+
g.Expect(err).NotTo(HaveOccurred())
79+
80+
got, err := ControlPlane().Initialized().Get(obj)
81+
g.Expect(err).ToNot(HaveOccurred())
82+
g.Expect(got).ToNot(BeNil())
83+
g.Expect(*got).To(Equal(true))
84+
})
5985
t.Run("Manages spec.replicas", func(t *testing.T) {
6086
g := NewWithT(t)
6187

@@ -108,6 +134,32 @@ func TestControlPlane(t *testing.T) {
108134
g.Expect(got).ToNot(BeNil())
109135
g.Expect(*got).To(Equal(int64(3)))
110136
})
137+
t.Run("Manages status.unavailableReplicas", func(t *testing.T) {
138+
g := NewWithT(t)
139+
140+
g.Expect(ControlPlane().UnavailableReplicas().Path()).To(Equal(Path{"status", "unavailableReplicas"}))
141+
142+
err := ControlPlane().UnavailableReplicas().Set(obj, int64(3))
143+
g.Expect(err).ToNot(HaveOccurred())
144+
145+
got, err := ControlPlane().UnavailableReplicas().Get(obj)
146+
g.Expect(err).ToNot(HaveOccurred())
147+
g.Expect(got).ToNot(BeNil())
148+
g.Expect(*got).To(Equal(int64(3)))
149+
})
150+
t.Run("Manages status.selector", func(t *testing.T) {
151+
g := NewWithT(t)
152+
153+
g.Expect(ControlPlane().Selector().Path()).To(Equal(Path{"status", "selector"}))
154+
155+
err := ControlPlane().Selector().Set(obj, "my-selector")
156+
g.Expect(err).ToNot(HaveOccurred())
157+
158+
got, err := ControlPlane().Selector().Get(obj)
159+
g.Expect(err).ToNot(HaveOccurred())
160+
g.Expect(got).ToNot(BeNil())
161+
g.Expect(*got).To(Equal("my-selector"))
162+
})
111163
t.Run("Manages spec.machineTemplate.infrastructureRef", func(t *testing.T) {
112164
g := NewWithT(t)
113165

0 commit comments

Comments
 (0)