Skip to content

Commit 114f4e7

Browse files
committed
MEDIUM: create a crd k8s job
this is needed due to limitations on helm chart where CRD upgrades do not work properly
1 parent e002cce commit 114f4e7

File tree

19 files changed

+5998
-5462
lines changed

19 files changed

+5998
-5462
lines changed

api/definition/crd-check.go

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Copyright 2023 HAProxy Technologies 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 definition
15+
16+
import (
17+
"context"
18+
"fmt"
19+
"os"
20+
"path/filepath"
21+
22+
"github.com/Masterminds/semver/v3"
23+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
24+
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
25+
api_error "k8s.io/apimachinery/pkg/api/errors"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/client-go/rest"
28+
"k8s.io/client-go/tools/clientcmd"
29+
"sigs.k8s.io/yaml"
30+
)
31+
32+
func homeDir() string {
33+
if h := os.Getenv("HOME"); h != "" {
34+
return h
35+
}
36+
return os.Getenv("USERPROFILE") // windows
37+
}
38+
39+
func restConfig(external bool) (restConfig *rest.Config, err error) { //revive:disable:flag-parameter
40+
if external {
41+
kubeconfig := filepath.Join(homeDir(), ".kube", "config")
42+
restConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
43+
} else {
44+
restConfig, err = rest.InClusterConfig()
45+
}
46+
if err != nil {
47+
return restConfig, err
48+
}
49+
return restConfig, err
50+
}
51+
52+
// CRDRefresh refreshes the CRDs from the configuration file.
53+
// If the CRD does not exist, it creates it.
54+
// If the CRD exists, it checks if it needs to be upgraded.
55+
// If the CRD needs to be upgraded, it upgrades the CRD.
56+
// The function prints the name of the CRD, if it exists, if it needs to be upgraded and if it was upgraded.
57+
func CRDRefresh(external bool) error { //revive:disable:function-length
58+
fmt.Print(hugCRDUpdater)
59+
fmt.Println() // yes, linter complains during tests, wow
60+
fmt.Println("checking CRDs")
61+
config, err := restConfig(external)
62+
if err != nil {
63+
return err
64+
}
65+
66+
// Create a new clientset for the apiextensions API group
67+
clientset := apiextensionsclientset.NewForConfigOrDie(config)
68+
69+
// Check if the CRD exists
70+
crds := getCRDs()
71+
for crdName, crdDef := range crds {
72+
// CustomResourceDefinition object
73+
var crd apiextensionsv1.CustomResourceDefinition
74+
err = yaml.Unmarshal(crdDef, &crd)
75+
if err != nil {
76+
return err
77+
}
78+
fmt.Println()
79+
fmt.Println("checking CRD ", crdName)
80+
81+
existingVersion, err := clientset.ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), crdName, metav1.GetOptions{})
82+
if err != nil {
83+
if !api_error.IsNotFound(err) {
84+
return err
85+
}
86+
fmt.Println("CRD " + crdName + " does not exist")
87+
// Create the CRD
88+
_, err = clientset.ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), &crd, metav1.CreateOptions{})
89+
if err != nil {
90+
return err
91+
}
92+
fmt.Println("CRD " + crdName + " created")
93+
continue
94+
}
95+
fmt.Println("CRD " + crdName + " exists")
96+
versions := existingVersion.Spec.Versions
97+
if len(versions) < 1 {
98+
fmt.Println("CRD ", crdName, " is empty ?")
99+
continue
100+
}
101+
// check if we have v1 and newest CN version
102+
crd.ObjectMeta.ResourceVersion = existingVersion.ObjectMeta.ResourceVersion
103+
if versions[0].Name == "v3" {
104+
cnInK8s, ok := getVersion(existingVersion.ObjectMeta.Annotations)
105+
106+
needUpgrade := false
107+
if !ok {
108+
needUpgrade = true
109+
}
110+
cnNew, _ := getVersion(crd.ObjectMeta.Annotations)
111+
vK8s, err := semver.NewVersion(cnInK8s)
112+
if err != nil {
113+
needUpgrade = true
114+
fmt.Println(err.Error())
115+
}
116+
vNew, err := semver.NewVersion(cnNew)
117+
if err != nil {
118+
needUpgrade = true
119+
fmt.Println(err.Error())
120+
}
121+
if vNew == nil {
122+
vNew, _ = semver.NewVersion("0.0.0")
123+
}
124+
if vK8s == nil {
125+
vK8s, _ = semver.NewVersion("0.0.0")
126+
}
127+
fmt.Println("CRD", crdName, "exists as v3, [v"+vK8s.String()+"]")
128+
if needUpgrade || vNew.GreaterThan(vK8s) {
129+
// Upgrade the CRDl
130+
_, err = clientset.ApiextensionsV1().CustomResourceDefinitions().Update(context.Background(), &crd, metav1.UpdateOptions{})
131+
if err != nil {
132+
return err
133+
}
134+
fmt.Printf("CRD %s updated, [v%s] -> [v%s]\n", crdName, vK8s.String(), vNew.String())
135+
}
136+
continue
137+
}
138+
_, err = clientset.ApiextensionsV1().CustomResourceDefinitions().Update(context.Background(), &crd, metav1.UpdateOptions{})
139+
if err != nil {
140+
return err
141+
}
142+
fmt.Println("CRD", crdName, "updated")
143+
}
144+
145+
fmt.Println("")
146+
fmt.Println("CRD update done")
147+
return nil
148+
}
149+
150+
func getVersion(annotations map[string]string) (version string, ok bool) {
151+
version, ok = annotations["client-native.haproxy.org/version"]
152+
if ok {
153+
return version, ok
154+
}
155+
version, ok = annotations["gate.hug/version"]
156+
if ok {
157+
return version, ok
158+
}
159+
version, ok = annotations["conf.hug/version"]
160+
return version, ok
161+
}
162+
163+
// hugCRDUpdater console pretty print
164+
const hugCRDUpdater = `
165+
____ ____ ____ _ _ _ _
166+
/ ___| _ \| _ \ | | | |_ __ __| | __ _| |_ ___ _ __
167+
| | | |_) | | | | | | | | '_ \ / _` + "`" + ` |/ _` + "`" + ` | __/ _ \ '__|
168+
| |___| _ <| |_| | | |_| | |_) | (_| | (_| | || __/ |
169+
\____|_| \_\____/ \___/| .__/ \__,_|\__,_|\__\___|_|
170+
|_|
171+
172+
`

api/definition/definitions.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package definition
2+
3+
import _ "embed"
4+
5+
//go:embed gate.v3.haproxy.org_backends.yaml
6+
var backends []byte
7+
8+
//go:embed gate.v3.haproxy.org_hugconfs.yaml
9+
var hugConf []byte
10+
11+
//go:embed gate.v3.haproxy.org_huggates.yaml
12+
var hugGates []byte
13+
14+
// getCRDs returns a map of CRD definitions
15+
func getCRDs() map[string][]byte {
16+
return map[string][]byte{
17+
"backends.gate.v3.haproxy.org": backends,
18+
"huggates.gate.v3.haproxy.org": hugGates,
19+
"hugconfs.gate.v3.haproxy.org": hugConf,
20+
}
21+
}

0 commit comments

Comments
 (0)