Skip to content

Commit b4e665b

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 b4e665b

28 files changed

+1080
-190
lines changed

pkg/interfaces/interfaces.go

Lines changed: 4 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)

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/resources/common.go

Lines changed: 49 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,11 @@ func (b Base) Meta(paramName string) interface{} {
7176
return val
7277
}
7378

79+
// equalsToDefinition returns false since there is no definition to compare Base resource against.
80+
func (b Base) equalsToDefinition(_ interface{}) bool {
81+
return false
82+
}
83+
7484
// KindToResourceTemplate is a map mapping kind strings to empty structs representing proper resources
7585
// structs implement interfaces.ResourceTemplate
7686
var KindToResourceTemplate = map[string]interfaces.ResourceTemplate{}
@@ -154,7 +164,9 @@ func podsStateFromLabels(apiClient client.Interface, objLabels map[string]string
154164
resources := make([]interfaces.BaseResource, 0, len(pods.Items))
155165
for _, pod := range pods.Items {
156166
p := pod
157-
resources = append(resources, createNewPod(&p, apiClient.Pods(), nil))
167+
//resources = append(resources, createNewPod(&p, apiClient.Pods(), nil))
168+
def := MakeDefinition(&p)
169+
resources = append(resources, createNewPod(def, apiClient.Pods()))
158170
}
159171

160172
status, err := resourceListStatus(resources)
@@ -215,3 +227,35 @@ func parametrizeResource(resource interface{}, context interfaces.GraphContext,
215227
return value
216228
}, append(replaceIn, "ObjectMeta")...)
217229
}
230+
231+
func MakeDefinition(object interface{}) client.ResourceDefinition {
232+
result := client.ResourceDefinition{}
233+
234+
switch object.(type) {
235+
case *v1.ConfigMap:
236+
result.ConfigMap = object.(*v1.ConfigMap)
237+
case *extbeta1.DaemonSet:
238+
result.DaemonSet = object.(*extbeta1.DaemonSet)
239+
case *extbeta1.Deployment:
240+
result.Deployment = object.(*extbeta1.Deployment)
241+
case *batchv1.Job:
242+
result.Job = object.(*batchv1.Job)
243+
case *v1.PersistentVolumeClaim:
244+
result.PersistentVolumeClaim = object.(*v1.PersistentVolumeClaim)
245+
case *appsalpha1.PetSet:
246+
result.PetSet = object.(*appsalpha1.PetSet)
247+
case *v1.Pod:
248+
result.Pod = object.(*v1.Pod)
249+
case *extbeta1.ReplicaSet:
250+
result.ReplicaSet = object.(*extbeta1.ReplicaSet)
251+
case *v1.Secret:
252+
result.Secret = object.(*v1.Secret)
253+
case *v1.Service:
254+
result.Service = object.(*v1.Service)
255+
case *v1.ServiceAccount:
256+
result.ServiceAccount = object.(*v1.ServiceAccount)
257+
case *appsbeta1.StatefulSet:
258+
result.StatefulSet = object.(*appsbeta1.StatefulSet)
259+
}
260+
return result
261+
}

pkg/resources/configmap.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package resources
1616

1717
import (
1818
"log"
19+
"reflect"
1920

2021
"github.com/Mirantis/k8s-AppController/pkg/client"
2122
"github.com/Mirantis/k8s-AppController/pkg/interfaces"
@@ -58,8 +59,21 @@ func (configMapTemplateFactory) Kind() string {
5859

5960
// New returns configMap controller for new resource based on resource definition
6061
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()}}
62+
def.ConfigMap = parametrizeResource(def.ConfigMap, gc, configMapParamFields).(*v1.ConfigMap)
63+
return createNewConfigMap(def, c)
64+
}
65+
66+
func createNewConfigMap(def client.ResourceDefinition, c client.Interface) interfaces.Resource {
67+
return report.SimpleReporter{
68+
BaseResource: newConfigMap{
69+
Base: Base{
70+
Definition: def,
71+
meta: def.Meta,
72+
},
73+
ConfigMap: def.ConfigMap,
74+
Client: c.ConfigMaps(),
75+
},
76+
}
6377
}
6478

6579
// NewExisting returns configMap controller for existing resource by its name
@@ -76,18 +90,25 @@ func (c newConfigMap) Key() string {
7690
return configMapKey(c.ConfigMap.Name)
7791
}
7892

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

100+
if !c.equalsToDefinition(cm) {
101+
return interfaces.ResourceWaitingForUpgrade, nil
102+
}
103+
85104
return interfaces.ResourceReady, nil
86105
}
87106

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)
107+
// equalsToDefinition checks if definition in object is compatible with provided object
108+
func (c newConfigMap) equalsToDefinition(configmap interface{}) bool {
109+
cm := configmap.(*v1.ConfigMap)
110+
111+
return reflect.DeepEqual(cm.Data, c.Definition.ConfigMap.Data) && reflect.DeepEqual(cm.ObjectMeta, c.Definition.ConfigMap.ObjectMeta)
91112
}
92113

93114
// Create looks for DaemonSet in k8s and creates it if not present
@@ -110,9 +131,14 @@ func (c existingConfigMap) Key() string {
110131
return configMapKey(c.Name)
111132
}
112133

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

118144
// 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 found, expected nil")
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: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package resources
1616

1717
import (
1818
"log"
19+
"reflect"
1920

2021
"github.com/Mirantis/k8s-AppController/pkg/client"
2122
"github.com/Mirantis/k8s-AppController/pkg/interfaces"
@@ -58,8 +59,8 @@ func (daemonSetTemplateFactory) Kind() string {
5859

5960
// New returns DaemonSets controller for new resource based on resource definition
6061
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()}}
62+
def.DaemonSet = parametrizeResource(def.DaemonSet, gc, daemonSetParamFields).(*extbeta1.DaemonSet)
63+
return createNewDaemonSet(def, c.DaemonSets())
6364
}
6465

6566
// NewExisting returns DaemonSets controller for existing resource by its name
@@ -71,12 +72,8 @@ func daemonSetKey(name string) string {
7172
return "daemonset/" + name
7273
}
7374

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 {
75+
func daemonSetStatus(d *extbeta1.DaemonSet) (interfaces.ResourceStatus, error) {
76+
if d.Status.CurrentNumberScheduled == d.Status.DesiredNumberScheduled {
8077
return interfaces.ResourceReady, nil
8178
}
8279
return interfaces.ResourceNotReady, nil
@@ -89,7 +86,22 @@ func (d DaemonSet) Key() string {
8986

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

95107
// Create looks for DaemonSet in k8s and creates it if not present
@@ -107,6 +119,19 @@ func (d DaemonSet) Delete() error {
107119
return d.Client.Delete(d.DaemonSet.Name, &v1.DeleteOptions{})
108120
}
109121

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

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

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

0 commit comments

Comments
 (0)