Skip to content

Commit 18a7e69

Browse files
committed
Add editing of CHIs
1 parent b1e28a2 commit 18a7e69

File tree

9 files changed

+427
-130
lines changed

9 files changed

+427
-130
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/emicklei/go-restful-openapi/v2 v2.6.0
99
github.com/emicklei/go-restful/v3 v3.7.1
1010
github.com/go-openapi/spec v0.20.4
11+
github.com/kubernetes-sigs/yaml v1.1.0 // indirect
1112
k8s.io/api v0.22.4
1213
k8s.io/apimachinery v0.22.3
1314
k8s.io/client-go v0.22.3

internal/api/api_model.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,27 @@ type OperatorPod struct {
3030
Version string `json:"version" description:"version of the pod"`
3131
}
3232

33+
type ResourceSpecMetadata struct {
34+
Name string `json:"name"`
35+
Namespace string `json:"namespace"`
36+
ResourceVersion string `json:"resourceVersion"`
37+
}
38+
39+
type ResourceSpec struct {
40+
APIVersion string `json:"apiVersion"`
41+
Kind string `json:"kind"`
42+
Metadata ResourceSpecMetadata `json:"metadata"`
43+
Spec interface{} `json:"spec"`
44+
}
45+
3346
type Chi struct {
3447
Name string `json:"name" description:"name of the ClickHouse installation"`
3548
Namespace string `json:"namespace" description:"namespace the installation is in"`
3649
Status string `json:"status" description:"status of the installation"`
3750
Clusters int `json:"clusters" description:"number of clusters in the installation"`
3851
Hosts int `json:"hosts" description:"number of hosts in the installation"`
3952
ExternalURL string `json:"external_url" description:"external URL of the loadbalancer service"`
53+
ResourceYAML string `json:"resource_yaml" description:"Kubernetes YAML spec of the CHI resource"`
4054
CHClusterPods []CHClusterPod `json:"ch_cluster_pods" description:"ClickHouse cluster pods"`
4155
}
4256

internal/api/chi.go

Lines changed: 93 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/altinity/altinity-dashboard/internal/utils"
88
chopv1 "github.com/altinity/clickhouse-operator/pkg/apis/clickhouse.altinity.com/v1"
99
"github.com/emicklei/go-restful/v3"
10+
"github.com/kubernetes-sigs/yaml"
1011
v1 "k8s.io/api/core/v1"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1213
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -46,12 +47,26 @@ func (c *ChiResource) WebService(_ *WebServiceInfo) (*restful.WebService, error)
4647
Writes([]Chi{}).
4748
Returns(200, "OK", []Chi{}))
4849

49-
ws.Route(ws.PUT("/{namespace}").To(c.handlePutCHI).
50-
Doc("deploy a ClickHouse Installation from YAML").
50+
ws.Route(ws.GET("/{namespace}/{name}").To(c.getCHIs).
51+
Doc("get a single ClickHouse Installation").
52+
Param(ws.PathParameter("namespace", "namespace to get from").DataType("string")).
53+
Param(ws.PathParameter("name", "name of the CHI to get").DataType("string")).
54+
Writes([]Chi{}).
55+
Returns(200, "OK", []Chi{}))
56+
57+
ws.Route(ws.POST("/{namespace}").To(c.handlePostCHI).
58+
Doc("deploy a new ClickHouse Installation from YAML").
5159
Param(ws.PathParameter("namespace", "namespace to deploy to").DataType("string")).
5260
Reads(ChiPutParams{}).
5361
Returns(200, "OK", nil))
5462

63+
ws.Route(ws.PATCH("/{namespace}/{name}").To(c.handlePatchCHI).
64+
Doc("update an existing ClickHouse Installation from YAML").
65+
Param(ws.PathParameter("namespace", "namespace the CHI is in").DataType("string")).
66+
Param(ws.PathParameter("name", "name of the CHI to update").DataType("string")).
67+
Reads(ChiPutParams{}).
68+
Returns(200, "OK", nil))
69+
5570
ws.Route(ws.DELETE("/{namespace}/{name}").To(c.handleDeleteCHI).
5671
Doc("delete a ClickHouse installation").
5772
Param(ws.PathParameter("namespace", "namespace to delete from").DataType("string")).
@@ -66,10 +81,20 @@ func (c *ChiResource) getCHIs(request *restful.Request, response *restful.Respon
6681
if !ok {
6782
namespace = ""
6883
}
84+
name, ok := request.PathParameters()["name"]
85+
if !ok {
86+
name = ""
87+
}
6988

7089
k := utils.GetK8s()
90+
var fieldSelector string
91+
if name != "" {
92+
fieldSelector = "metadata.name=" + name
93+
}
7194
chis, err := k.ChopClientset.ClickhouseV1().ClickHouseInstallations(namespace).List(
72-
context.TODO(), metav1.ListOptions{})
95+
context.TODO(), metav1.ListOptions{
96+
FieldSelector: fieldSelector,
97+
})
7398
if err != nil {
7499
webError(response, http.StatusBadRequest, "listing CHIs", err)
75100
return
@@ -85,7 +110,8 @@ func (c *ChiResource) getCHIs(request *restful.Request, response *restful.Respon
85110
},
86111
MatchExpressions: nil,
87112
}
88-
pods, err := getK8sPodsFromLabelSelector(chi.Namespace, sel)
113+
var pods *v1.PodList
114+
pods, err = getK8sPodsFromLabelSelector(chi.Namespace, sel)
89115
if err == nil {
90116
for _, pod := range getPodsFromK8sPods(pods) {
91117
chClusterPod := CHClusterPod{
@@ -130,65 +156,107 @@ func (c *ChiResource) getCHIs(request *restful.Request, response *restful.Respon
130156
}
131157
}
132158
}
159+
var y []byte
160+
y, err = yaml.Marshal(ResourceSpec{
161+
APIVersion: chi.APIVersion,
162+
Kind: chi.Kind,
163+
Metadata: ResourceSpecMetadata{
164+
Name: chi.Name,
165+
Namespace: chi.Namespace,
166+
ResourceVersion: chi.ResourceVersion,
167+
},
168+
Spec: chi.Spec,
169+
})
170+
if err != nil {
171+
y = nil
172+
}
133173
list = append(list, Chi{
134174
Name: chi.Name,
135175
Namespace: chi.Namespace,
136176
Status: chi.Status.Status,
137177
Clusters: chi.Status.ClustersCount,
138178
Hosts: chi.Status.HostsCount,
139179
ExternalURL: externalURL,
180+
ResourceYAML: string(y),
140181
CHClusterPods: chClusterPods,
141182
})
142183
}
143184
_ = response.WriteEntity(list)
144185
}
145186

146187
var ErrNamespaceRequired = errors.New("namespace is required")
147-
var ErrNameAndNamespaceRequired = errors.New("name and namespace are required")
188+
var ErrNameRequired = errors.New("name is required")
148189
var ErrYAMLMustBeCHI = errors.New("YAML document must contain a single ClickhouseInstallation definition")
149190

150-
func (c *ChiResource) handlePutCHI(request *restful.Request, response *restful.Response) {
191+
func (c *ChiResource) handlePostOrPatchCHI(request *restful.Request, response *restful.Response, doPost bool) {
151192
namespace, ok := request.PathParameters()["namespace"]
152193
if !ok || namespace == "" {
153194
webError(response, http.StatusBadRequest, "processing request", ErrNamespaceRequired)
154195
return
155196
}
197+
name := ""
198+
if !doPost {
199+
name, ok = request.PathParameters()["name"]
200+
if !ok || name == "" {
201+
webError(response, http.StatusBadRequest, "processing request", ErrNameRequired)
202+
return
203+
}
204+
}
156205

157206
putParams := ChiPutParams{}
158207
err := request.ReadEntity(&putParams)
159208
if err != nil {
160209
webError(response, http.StatusBadRequest, "reading request body", err)
161210
return
162211
}
163-
var rejected = false
164-
err = utils.GetK8s().DoApplySelectively(putParams.YAML, namespace,
165-
func(candidates []*unstructured.Unstructured) []*unstructured.Unstructured {
166-
if len(candidates) != 1 {
167-
rejected = true
168-
return nil
169-
}
170-
if candidates[0].GetKind() != "ClickHouseInstallation" {
171-
rejected = true
172-
return nil
173-
}
174-
return candidates
175-
})
176-
if rejected {
212+
213+
k := utils.GetK8s()
214+
var obj *unstructured.Unstructured
215+
obj, err = k.DecodeYAMLToObject(putParams.YAML)
216+
if err != nil {
217+
webError(response, http.StatusBadRequest, "parsing YAML object", err)
218+
return
219+
}
220+
if obj.GetAPIVersion() != "clickhouse.altinity.com/v1" ||
221+
obj.GetKind() != "ClickHouseInstallation" ||
222+
(!doPost && (obj.GetNamespace() != namespace ||
223+
obj.GetName() != name)) {
177224
webError(response, http.StatusBadRequest, "processing request", ErrYAMLMustBeCHI)
178225
return
179226
}
227+
var errSource string
228+
if doPost {
229+
errSource = "creating CHI"
230+
err = k.SingleObjectCreate(obj, namespace)
231+
} else {
232+
errSource = "updating CHI"
233+
err = k.SingleObjectUpdate(obj, namespace)
234+
}
180235
if err != nil {
181-
webError(response, http.StatusInternalServerError, "applying CHI", err)
236+
webError(response, http.StatusInternalServerError, errSource, err)
182237
return
183238
}
184239
_ = response.WriteEntity(nil)
185240
}
186241

242+
func (c *ChiResource) handlePostCHI(request *restful.Request, response *restful.Response) {
243+
c.handlePostOrPatchCHI(request, response, true)
244+
}
245+
246+
func (c *ChiResource) handlePatchCHI(request *restful.Request, response *restful.Response) {
247+
c.handlePostOrPatchCHI(request, response, false)
248+
}
249+
187250
func (c *ChiResource) handleDeleteCHI(request *restful.Request, response *restful.Response) {
188-
namespace, ok1 := request.PathParameters()["namespace"]
189-
name, ok2 := request.PathParameters()["name"]
190-
if !ok1 || !ok2 || name == "" || namespace == "" {
191-
webError(response, http.StatusBadRequest, "processing request", ErrNameAndNamespaceRequired)
251+
namespace, ok := request.PathParameters()["namespace"]
252+
if !ok || namespace == "" {
253+
webError(response, http.StatusBadRequest, "processing request", ErrNamespaceRequired)
254+
return
255+
}
256+
var name string
257+
name, ok = request.PathParameters()["name"]
258+
if !ok || name == "" {
259+
webError(response, http.StatusBadRequest, "processing request", ErrNameRequired)
192260
return
193261
}
194262

internal/api/operator.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func (o *OperatorResource) deployOrDeleteOperator(namespace string, version stri
182182
// Delete cluster-wide resources if we're deleting the last operator
183183
namespace = ""
184184
}
185-
err = k.DoDelete(deploy, namespace)
185+
err = k.MultiYamlDelete(deploy, namespace)
186186
if err != nil {
187187
return err
188188
}
@@ -194,7 +194,7 @@ func (o *OperatorResource) deployOrDeleteOperator(namespace string, version stri
194194
}
195195
}
196196
if isUpgrade {
197-
err = k.DoApplySelectively(deploy, namespace,
197+
err = k.MultiYamlApplySelectively(deploy, namespace,
198198
func(candidates []*unstructured.Unstructured) []*unstructured.Unstructured {
199199
selected := make([]*unstructured.Unstructured, 0)
200200
for _, c := range candidates {
@@ -208,7 +208,7 @@ func (o *OperatorResource) deployOrDeleteOperator(namespace string, version stri
208208
return err
209209
}
210210
} else {
211-
err = k.DoApply(deploy, namespace)
211+
err = k.MultiYamlApply(deploy, namespace)
212212
if err != nil {
213213
return err
214214
}

0 commit comments

Comments
 (0)