/
create.go
136 lines (118 loc) · 4.4 KB
/
create.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package status
import (
"context"
"fmt"
"strings"
appv1alpha1 "github.com/giantswarm/apiextensions-application/api/v1alpha1"
"github.com/giantswarm/microerror"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/client"
releasev1alpha1 "github.com/giantswarm/release-operator/v3/api/v1alpha1"
"github.com/giantswarm/release-operator/v3/pkg/project"
"github.com/giantswarm/release-operator/v3/service/controller/key"
)
func (r *Resource) EnsureCreated(ctx context.Context, obj interface{}) error {
release, err := key.ToReleaseCR(obj)
if err != nil {
return microerror.Mask(err)
}
if release.DeletionTimestamp != nil {
return nil
}
components := key.FilterComponents(release.Spec.Components)
var apps appv1alpha1.AppList
{
err := r.k8sClient.CtrlClient().List(
ctx,
&apps,
&client.ListOptions{
LabelSelector: labels.SelectorFromSet(labels.Set{
key.LabelManagedBy: project.Name(),
}),
},
)
if err != nil {
return microerror.Mask(err)
}
}
// Doing this per-release isn't ideal, can we pass this list to each status reconcile somehow?
var tenantClusters []tenantCluster
{
r.logger.LogCtx(ctx, "level", "debug", "message", "searching for running tenant clusters")
var err error
tenantClusters, err = r.getCurrentTenantClusters(ctx)
if err != nil {
r.logger.LogCtx(ctx, "level", "error", "message", fmt.Sprintf("error finding tenant clusters: %s", err))
} else {
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("found %d running tenant clusters", len(tenantClusters)))
}
}
var releaseInUse bool
{
// Get two sets of just deduplicated versions
releaseVersions, operatorVersions := consolidateClusterVersions(tenantClusters)
// Check the set of release versions and keep this release if it is used.
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("checking release %s", release.Name))
if releaseVersions[strings.TrimPrefix(release.Name, "v")] { // The release name has a leading `v`
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("keeping release %s because it is explicitly used", release.Name))
releaseInUse = true
} else {
for _, o := range key.GetProviderOperators() {
operatorVersion := getOperatorVersionInRelease(o, release)
// Check the set of operator versions and keep this release if its operator version is used.
if operatorVersion != "" && operatorVersions[o][operatorVersion] {
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("keeping release %s because a cluster using its operator version (%s) is present", release.Name, operatorVersion))
releaseInUse = true
}
}
}
// We don't want to unschedule a kvm-operator with no releases in use as long as it has pods it needs to drain.
// We check the kvm-operator version (blank if this is an AWS/Azure installation), find all pods with that
// version bundle version annotation and consider the release to be in use if any were found.
if !releaseInUse { // small optimization
operatorVersion := getOperatorVersionInRelease(key.ProviderOperatorKVM, release)
if operatorVersion != "" { // only execute on KVM
kvmPodsExist, err := r.getKVMOperatorVersionPodsExist(ctx, operatorVersion)
if err != nil {
return microerror.Mask(err)
}
if kvmPodsExist {
releaseInUse = true
}
}
}
}
var releaseDeployed bool
{
releaseDeployed = true
for _, component := range components {
if !key.ComponentAppDeployed(component, apps.Items) {
releaseDeployed = false
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("component %#q in release %#q is not deployed", component.Name, release.Name))
}
}
}
{
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("setting status for release %#q", release.Name))
release.Status.Ready = releaseDeployed
release.Status.InUse = releaseInUse
err := r.k8sClient.CtrlClient().Status().Update(
ctx,
release,
)
if err != nil {
return microerror.Mask(err)
}
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("status set for release %#q", release.Name))
}
return nil
}
// Searches the components in a release for the given operator and returns the version.
func getOperatorVersionInRelease(operator string, release *releasev1alpha1.Release) string {
for _, component := range release.Spec.Components {
if component.Name == operator {
return component.Version
}
}
return ""
}