Skip to content

Commit 2b5706a

Browse files
committed
Status checks if resource was upgraded
All resources now have a Definition to which they need to conform in order to make an actual status check
1 parent 080633e commit 2b5706a

30 files changed

+1107
-190
lines changed

pkg/interfaces/interfaces.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ type ResourceStatus string
2323

2424
// Possible ResourceStatus values
2525
const (
26-
ResourceReady ResourceStatus = "ready"
27-
ResourceNotReady ResourceStatus = "not ready"
28-
ResourceError ResourceStatus = "error"
26+
ResourceReady ResourceStatus = "ready"
27+
ResourceNotReady ResourceStatus = "not ready"
28+
ResourceError ResourceStatus = "error"
29+
ResourceWaitingForUpgrade ResourceStatus = "waiting for upgrade"
2930
)
3031

3132
// DefaultFlowName is the name of default flow (main dependency graph)
@@ -39,6 +40,7 @@ type BaseResource interface {
3940
Create() error
4041
Delete() error
4142
Meta(string) interface{}
43+
EqualToDefinition(interface{}) bool
4244
}
4345

4446
// DependencyReport is a report of a single dependency of a node in graph

pkg/mocks/countingresource.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ func (c *CountingResource) NewExisting(name string, _ client.Interface) interfac
8080
return report.SimpleReporter{BaseResource: NewResource(name, interfaces.ResourceReady)}
8181
}
8282

83+
// EqualToDefinition returns is false
84+
func (c *CountingResource) EqualToDefinition(_ interface{}) bool {
85+
return false
86+
}
87+
8388
// NewCountingResource creates new instance of CountingResource
8489
func NewCountingResource(key string, counter *CounterWithMemo, timeout time.Duration) *CountingResource {
8590
return &CountingResource{

pkg/mocks/daemonsets.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414

1515
package mocks
1616

17-
import extbeta1 "k8s.io/client-go/pkg/apis/extensions/v1beta1"
17+
import (
18+
extbeta1 "k8s.io/client-go/pkg/apis/extensions/v1beta1"
19+
"k8s.io/client-go/pkg/runtime"
20+
)
1821

1922
type daemonSetClient struct {
2023
}
@@ -33,3 +36,11 @@ func MakeDaemonSet(name string) *extbeta1.DaemonSet {
3336

3437
return daemonSet
3538
}
39+
40+
func DaemonSets(names ...string) runtime.Object {
41+
var daemonsets []extbeta1.DaemonSet
42+
for _, name := range names {
43+
daemonsets = append(daemonsets, *MakeDaemonSet(name))
44+
}
45+
return &extbeta1.DaemonSetList{Items: daemonsets}
46+
}

pkg/mocks/resource.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ func (c *Resource) NewExisting(name string, _ client.Interface) interfaces.BaseR
6767
return NewResource(name, "ready")
6868
}
6969

70+
// EqualToDefinition returns is false
71+
func (c *Resource) EqualToDefinition(_ interface{}) bool {
72+
return false
73+
}
74+
7075
// NewResource creates new instance of Resource
7176
func NewResource(key string, status interfaces.ResourceStatus) *Resource {
7277
return NewResourceWithMeta(key, status, nil)

pkg/resources/common.go

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ import (
2121
"strconv"
2222
"strings"
2323

24+
"k8s.io/client-go/pkg/api/v1"
25+
appsbeta1 "k8s.io/client-go/pkg/apis/apps/v1beta1"
26+
batchv1 "k8s.io/client-go/pkg/apis/batch/v1"
27+
extbeta1 "k8s.io/client-go/pkg/apis/extensions/v1beta1"
28+
"k8s.io/client-go/pkg/labels"
29+
2430
"github.com/Mirantis/k8s-AppController/pkg/client"
31+
appsalpha1 "github.com/Mirantis/k8s-AppController/pkg/client/petsets/apis/apps/v1alpha1"
2532
"github.com/Mirantis/k8s-AppController/pkg/copier"
2633
"github.com/Mirantis/k8s-AppController/pkg/interfaces"
27-
28-
"k8s.io/client-go/pkg/api/v1"
29-
"k8s.io/client-go/pkg/labels"
3034
)
3135

3236
func init() {
@@ -52,7 +56,8 @@ func init() {
5256

5357
// Base is a base struct that contains data common for all resources
5458
type Base struct {
55-
meta map[string]interface{}
59+
Definition client.ResourceDefinition
60+
meta map[string]interface{}
5661
}
5762

5863
// Meta returns metadata parameter with given name, or empty string,
@@ -71,6 +76,13 @@ func (b Base) Meta(paramName string) interface{} {
7176
return val
7277
}
7378

79+
// EqualToDefinition returns false since there is no definition to compare Base resource against.
80+
func (b Base) EqualToDefinition(_ interface{}) bool {
81+
//TODO: remove
82+
panic("trololo")
83+
return false
84+
}
85+
7486
// KindToResourceTemplate is a map mapping kind strings to empty structs representing proper resources
7587
// structs implement interfaces.ResourceTemplate
7688
var KindToResourceTemplate = map[string]interfaces.ResourceTemplate{}
@@ -154,7 +166,9 @@ func podsStateFromLabels(apiClient client.Interface, objLabels map[string]string
154166
resources := make([]interfaces.BaseResource, 0, len(pods.Items))
155167
for _, pod := range pods.Items {
156168
p := pod
157-
resources = append(resources, createNewPod(&p, apiClient.Pods(), nil))
169+
//resources = append(resources, createNewPod(&p, apiClient.Pods(), nil))
170+
def := MakeDefinition(&p)
171+
resources = append(resources, createNewPod(def, apiClient.Pods()))
158172
}
159173

160174
status, err := resourceListStatus(resources)
@@ -215,3 +229,38 @@ func parametrizeResource(resource interface{}, context interfaces.GraphContext,
215229
return value
216230
}, append(replaceIn, "ObjectMeta")...)
217231
}
232+
233+
func MakeDefinition(object interface{}) client.ResourceDefinition {
234+
result := client.ResourceDefinition{}
235+
236+
switch object.(type) {
237+
case *v1.ConfigMap:
238+
result.ConfigMap = object.(*v1.ConfigMap)
239+
case *extbeta1.DaemonSet:
240+
result.DaemonSet = object.(*extbeta1.DaemonSet)
241+
case *extbeta1.Deployment:
242+
result.Deployment = object.(*extbeta1.Deployment)
243+
case *batchv1.Job:
244+
result.Job = object.(*batchv1.Job)
245+
case *v1.PersistentVolumeClaim:
246+
result.PersistentVolumeClaim = object.(*v1.PersistentVolumeClaim)
247+
case *appsalpha1.PetSet:
248+
result.PetSet = object.(*appsalpha1.PetSet)
249+
case *v1.Pod:
250+
result.Pod = object.(*v1.Pod)
251+
case *extbeta1.ReplicaSet:
252+
result.ReplicaSet = object.(*extbeta1.ReplicaSet)
253+
case *v1.Secret:
254+
result.Secret = object.(*v1.Secret)
255+
case *v1.Service:
256+
result.Service = object.(*v1.Service)
257+
case *v1.ServiceAccount:
258+
result.ServiceAccount = object.(*v1.ServiceAccount)
259+
case *appsbeta1.StatefulSet:
260+
result.StatefulSet = object.(*appsbeta1.StatefulSet)
261+
default:
262+
//TODO: remove it
263+
panic("no type for definition")
264+
}
265+
return result
266+
}

pkg/resources/configmap.go

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
package resources
1616

1717
import (
18+
"fmt"
1819
"log"
20+
"reflect"
1921

2022
"github.com/Mirantis/k8s-AppController/pkg/client"
2123
"github.com/Mirantis/k8s-AppController/pkg/interfaces"
@@ -58,8 +60,21 @@ func (configMapTemplateFactory) Kind() string {
5860

5961
// New returns configMap controller for new resource based on resource definition
6062
func (configMapTemplateFactory) New(def client.ResourceDefinition, c client.Interface, gc interfaces.GraphContext) interfaces.Resource {
61-
cm := parametrizeResource(def.ConfigMap, gc, configMapParamFields).(*v1.ConfigMap)
62-
return report.SimpleReporter{BaseResource: newConfigMap{Base: Base{def.Meta}, ConfigMap: cm, Client: c.ConfigMaps()}}
63+
def.ConfigMap = parametrizeResource(def.ConfigMap, gc, configMapParamFields).(*v1.ConfigMap)
64+
return createNewConfigMap(def, c)
65+
}
66+
67+
func createNewConfigMap(def client.ResourceDefinition, c client.Interface) interfaces.Resource {
68+
return report.SimpleReporter{
69+
BaseResource: newConfigMap{
70+
Base: Base{
71+
Definition: def,
72+
meta: def.Meta,
73+
},
74+
ConfigMap: def.ConfigMap,
75+
Client: c.ConfigMaps(),
76+
},
77+
}
6378
}
6479

6580
// NewExisting returns configMap controller for existing resource by its name
@@ -76,18 +91,25 @@ func (c newConfigMap) Key() string {
7691
return configMapKey(c.ConfigMap.Name)
7792
}
7893

79-
func configMapStatus(c corev1.ConfigMapInterface, name string) (interfaces.ResourceStatus, error) {
80-
_, err := c.Get(name)
94+
// Status returns ConfigMap status. interfaces.ResourceReady means that its dependencies can be created
95+
func (c newConfigMap) Status(meta map[string]string) (interfaces.ResourceStatus, error) {
96+
cm, err := c.Client.Get(c.ConfigMap.Name)
8197
if err != nil {
8298
return interfaces.ResourceError, err
8399
}
84100

101+
if !c.EqualToDefinition(cm) {
102+
return interfaces.ResourceWaitingForUpgrade, fmt.Errorf(string(interfaces.ResourceWaitingForUpgrade))
103+
}
104+
85105
return interfaces.ResourceReady, nil
86106
}
87107

88-
// Status returns configMap status. interfaces.ResourceReady means that its dependencies can be created
89-
func (c newConfigMap) Status(meta map[string]string) (interfaces.ResourceStatus, error) {
90-
return configMapStatus(c.Client, c.ConfigMap.Name)
108+
// EqualToDefinition checks if definition in object is compatible with provided object
109+
func (c newConfigMap) EqualToDefinition(configmap interface{}) bool {
110+
cm := configmap.(*v1.ConfigMap)
111+
112+
return reflect.DeepEqual(cm.Data, c.Definition.ConfigMap.Data) && reflect.DeepEqual(cm.ObjectMeta, c.Definition.ConfigMap.ObjectMeta)
91113
}
92114

93115
// Create looks for DaemonSet in k8s and creates it if not present
@@ -110,9 +132,14 @@ func (c existingConfigMap) Key() string {
110132
return configMapKey(c.Name)
111133
}
112134

113-
// Status returns configMap status. interfaces.ResourceReady means that its dependencies can be created
135+
// Status returns ConfigMap status. interfaces.ResourceReady means that its dependencies can be created
114136
func (c existingConfigMap) Status(meta map[string]string) (interfaces.ResourceStatus, error) {
115-
return configMapStatus(c.Client, c.Name)
137+
_, err := c.Client.Get(c.Name)
138+
if err != nil {
139+
return interfaces.ResourceError, err
140+
}
141+
142+
return interfaces.ResourceReady, nil
116143
}
117144

118145
// Create looks for existing configMap and returns an error if there is no such configMap in a cluster

pkg/resources/configmap_test.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ import (
2323

2424
// TestConfigMapSuccessCheck checks status of ready ConfigMap
2525
func TestConfigMapSuccessCheck(t *testing.T) {
26-
c := mocks.NewClient(mocks.ConfigMaps("notfail"))
27-
status, err := configMapStatus(c.ConfigMaps(), "notfail")
26+
name := "notfail"
27+
client := mocks.NewClient(mocks.ConfigMaps(name))
28+
29+
def := MakeDefinition(mocks.MakeConfigMap(name))
30+
tested := createNewConfigMap(def, client)
31+
32+
status, err := tested.Status(nil)
2833

2934
if err != nil {
3035
t.Error(err)
@@ -37,8 +42,13 @@ func TestConfigMapSuccessCheck(t *testing.T) {
3742

3843
// TestConfigMapFailCheck checks status of not existing ConfigMap
3944
func TestConfigMapFailCheck(t *testing.T) {
40-
c := mocks.NewClient(mocks.ConfigMaps())
41-
status, err := configMapStatus(c.ConfigMaps(), "fail")
45+
name := "fail"
46+
client := mocks.NewClient()
47+
48+
def := MakeDefinition(mocks.MakeConfigMap(name))
49+
tested := createNewConfigMap(def, client)
50+
51+
status, err := tested.Status(nil)
4252

4353
if err == nil {
4454
t.Error("error not found, expected error")
@@ -48,3 +58,27 @@ func TestConfigMapFailCheck(t *testing.T) {
4858
t.Errorf("status should be `error`, is `%s` instead.", status)
4959
}
5060
}
61+
62+
// TestConfigMapUpgraded tests status behaviour with resource definition differing from object in cluster
63+
func TestConfigMapUpgraded(t *testing.T) {
64+
name := "notfail"
65+
client := mocks.NewClient(mocks.ConfigMaps(name))
66+
67+
def := MakeDefinition(mocks.MakeConfigMap(name))
68+
69+
//Make definition differ from client version
70+
def.ConfigMap.ObjectMeta.Labels = map[string]string{
71+
"trolo": "lolo",
72+
}
73+
74+
tested := createNewConfigMap(def, client)
75+
76+
status, err := tested.Status(nil)
77+
78+
if err == nil {
79+
t.Error("Error not found, expected error")
80+
}
81+
if status != interfaces.ResourceWaitingForUpgrade {
82+
t.Errorf("Status should be `waiting for upgrade`, is `%s` instead.", status)
83+
}
84+
}

pkg/resources/daemonset.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
package resources
1616

1717
import (
18+
"fmt"
1819
"log"
20+
"reflect"
1921

2022
"github.com/Mirantis/k8s-AppController/pkg/client"
2123
"github.com/Mirantis/k8s-AppController/pkg/interfaces"
@@ -58,8 +60,8 @@ func (daemonSetTemplateFactory) Kind() string {
5860

5961
// New returns DaemonSets controller for new resource based on resource definition
6062
func (d daemonSetTemplateFactory) New(def client.ResourceDefinition, c client.Interface, gc interfaces.GraphContext) interfaces.Resource {
61-
newDaemonSet := parametrizeResource(def.DaemonSet, gc, daemonSetParamFields).(*extbeta1.DaemonSet)
62-
return report.SimpleReporter{BaseResource: DaemonSet{Base: Base{def.Meta}, DaemonSet: newDaemonSet, Client: c.DaemonSets()}}
63+
def.DaemonSet = parametrizeResource(def.DaemonSet, gc, daemonSetParamFields).(*extbeta1.DaemonSet)
64+
return createNewDaemonSet(def, c.DaemonSets())
6365
}
6466

6567
// NewExisting returns DaemonSets controller for existing resource by its name
@@ -71,12 +73,8 @@ func daemonSetKey(name string) string {
7173
return "daemonset/" + name
7274
}
7375

74-
func daemonSetStatus(d v1beta1.DaemonSetInterface, name string) (interfaces.ResourceStatus, error) {
75-
daemonSet, err := d.Get(name)
76-
if err != nil {
77-
return interfaces.ResourceError, err
78-
}
79-
if daemonSet.Status.CurrentNumberScheduled == daemonSet.Status.DesiredNumberScheduled {
76+
func daemonSetStatus(d *extbeta1.DaemonSet) (interfaces.ResourceStatus, error) {
77+
if d.Status.CurrentNumberScheduled == d.Status.DesiredNumberScheduled {
8078
return interfaces.ResourceReady, nil
8179
}
8280
return interfaces.ResourceNotReady, nil
@@ -89,7 +87,22 @@ func (d DaemonSet) Key() string {
8987

9088
// Status returns DaemonSet status. interfaces.ResourceReady means that its dependencies can be created
9189
func (d DaemonSet) Status(meta map[string]string) (interfaces.ResourceStatus, error) {
92-
return daemonSetStatus(d.Client, d.DaemonSet.Name)
90+
ds, err := d.Client.Get(d.DaemonSet.Name)
91+
if err != nil {
92+
return interfaces.ResourceError, err
93+
}
94+
95+
if !d.EqualToDefinition(ds) {
96+
return interfaces.ResourceWaitingForUpgrade, fmt.Errorf(string(interfaces.ResourceWaitingForUpgrade))
97+
}
98+
return daemonSetStatus(ds)
99+
}
100+
101+
// EqualToDefinition returns whether the resource has the same values as provided object
102+
func (d DaemonSet) EqualToDefinition(daemonset interface{}) bool {
103+
ds := daemonset.(*extbeta1.DaemonSet)
104+
105+
return reflect.DeepEqual(ds.ObjectMeta, d.DaemonSet.ObjectMeta) && reflect.DeepEqual(ds.Spec, d.DaemonSet.Spec)
93106
}
94107

95108
// Create looks for DaemonSet in k8s and creates it if not present
@@ -107,6 +120,19 @@ func (d DaemonSet) Delete() error {
107120
return d.Client.Delete(d.DaemonSet.Name, &v1.DeleteOptions{})
108121
}
109122

123+
func createNewDaemonSet(def client.ResourceDefinition, client v1beta1.DaemonSetInterface) interfaces.Resource {
124+
return report.SimpleReporter{
125+
BaseResource: DaemonSet{
126+
Base: Base{
127+
Definition: def,
128+
meta: def.Meta,
129+
},
130+
DaemonSet: def.DaemonSet,
131+
Client: client,
132+
},
133+
}
134+
}
135+
110136
// ExistingDaemonSet is a wrapper for K8s DaemonSet object which is deployed on a cluster before AppController
111137
type ExistingDaemonSet struct {
112138
Base
@@ -121,7 +147,12 @@ func (d ExistingDaemonSet) Key() string {
121147

122148
// Status returns DaemonSet status. interfaces.ResourceReady means that its dependencies can be created
123149
func (d ExistingDaemonSet) Status(meta map[string]string) (interfaces.ResourceStatus, error) {
124-
return daemonSetStatus(d.Client, d.Name)
150+
ds, err := d.Client.Get(d.Name)
151+
if err != nil {
152+
return interfaces.ResourceError, err
153+
}
154+
155+
return daemonSetStatus(ds)
125156
}
126157

127158
// Create looks for existing DaemonSet and returns error if there is no such DaemonSet

0 commit comments

Comments
 (0)