Skip to content

Commit 82f6914

Browse files
committed
implement template store management
1 parent 026005f commit 82f6914

File tree

10 files changed

+225
-9
lines changed

10 files changed

+225
-9
lines changed

cyclops-ctrl/internal/cluster/k8sclient/templatestore.go

+9
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,12 @@ import (
88
func (k *KubernetesClient) ListTemplateStore() ([]cyclopsv1alpha1.TemplateStore, error) {
99
return k.moduleset.TemplateStore(cyclopsNamespace).List(metav1.ListOptions{})
1010
}
11+
12+
func (k *KubernetesClient) CreateTemplateStore(ts *cyclopsv1alpha1.TemplateStore) error {
13+
_, err := k.moduleset.TemplateStore(cyclopsNamespace).Create(ts)
14+
return err
15+
}
16+
17+
func (k *KubernetesClient) DeleteTemplateStore(name string) error {
18+
return k.moduleset.TemplateStore(cyclopsNamespace).Delete(name)
19+
}

cyclops-ctrl/internal/cluster/v1alpha1/templatestore.go

+54
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ package v1alpha1
33
import (
44
"context"
55
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
6+
"k8s.io/apimachinery/pkg/watch"
67
"k8s.io/client-go/rest"
8+
"time"
79

810
cyclopsv1alpha1 "github.com/cyclops-ui/cycops-ctrl/api/v1alpha1"
911
)
1012

1113
type TemplateStoreInterface interface {
1214
List(opts metav1.ListOptions) ([]cyclopsv1alpha1.TemplateStore, error)
1315
Get(name string) (*cyclopsv1alpha1.TemplateStore, error)
16+
Create(*cyclopsv1alpha1.TemplateStore) (*cyclopsv1alpha1.TemplateStore, error)
17+
Update(*cyclopsv1alpha1.TemplateStore) (*cyclopsv1alpha1.TemplateStore, error)
18+
Watch(opts metav1.ListOptions) (watch.Interface, error)
19+
Delete(name string) error
1420
}
1521

1622
type templateStoreClient struct {
@@ -42,3 +48,51 @@ func (c *templateStoreClient) Get(name string) (*cyclopsv1alpha1.TemplateStore,
4248

4349
return &result, err
4450
}
51+
52+
func (c *templateStoreClient) Create(project *cyclopsv1alpha1.TemplateStore) (*cyclopsv1alpha1.TemplateStore, error) {
53+
result := cyclopsv1alpha1.TemplateStore{}
54+
err := c.restClient.
55+
Post().
56+
Namespace(c.ns).
57+
Resource("templatestores").
58+
Body(project).
59+
Do(context.Background()).
60+
Into(&result)
61+
62+
return &result, err
63+
}
64+
65+
func (c *templateStoreClient) Update(templateStore *cyclopsv1alpha1.TemplateStore) (project *cyclopsv1alpha1.TemplateStore, err error) {
66+
result := &cyclopsv1alpha1.TemplateStore{}
67+
err = c.restClient.Put().
68+
Namespace(c.ns).
69+
Resource("templatestores").
70+
Name(templateStore.Name).
71+
Body(templateStore).
72+
Do(context.TODO()).
73+
Into(result)
74+
return
75+
}
76+
77+
func (c *templateStoreClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
78+
var timeout time.Duration
79+
if opts.TimeoutSeconds != nil {
80+
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
81+
}
82+
opts.Watch = true
83+
return c.restClient.Get().
84+
Namespace(c.ns).
85+
Resource("templatestores").
86+
Timeout(timeout).
87+
Watch(context.Background())
88+
}
89+
90+
func (c *templateStoreClient) Delete(name string) error {
91+
return c.restClient.
92+
Delete().
93+
Namespace(c.ns).
94+
Resource("templatestores").
95+
Name(name).
96+
Do(context.Background()).
97+
Error()
98+
}

cyclops-ctrl/internal/controller/templates.go

+20
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,23 @@ func (c *Templates) ListTemplatesStore(ctx *gin.Context) {
192192

193193
ctx.JSON(http.StatusOK, storeDTO)
194194
}
195+
196+
func (c *Templates) CreateTemplatesStore(ctx *gin.Context) {
197+
ctx.Header("Access-Control-Allow-Origin", "*")
198+
199+
var templateStore *dto.TemplateStore
200+
if err := ctx.ShouldBind(&templateStore); err != nil {
201+
fmt.Println("error binding request", templateStore)
202+
ctx.JSON(http.StatusBadRequest, dto.NewError("Error binding request", err.Error()))
203+
return
204+
}
205+
206+
k8sTemplateStore := mapper.DTOToTemplateStoreList(*templateStore)
207+
208+
if err := c.kubernetesClient.CreateTemplateStore(k8sTemplateStore); err != nil {
209+
ctx.JSON(http.StatusInternalServerError, dto.NewError("Error creating module", err.Error()))
210+
return
211+
}
212+
213+
ctx.Status(http.StatusCreated)
214+
}

cyclops-ctrl/internal/handler/handler.go

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func (h *Handler) Start() error {
5858

5959
// templates store
6060
h.router.GET("/templates/store", templatesController.ListTemplatesStore)
61+
h.router.PUT("/templates/store", templatesController.CreateTemplatesStore)
6162

6263
// modules
6364
h.router.GET("/modules/:name", modulesController.GetModule)

cyclops-ctrl/internal/mapper/templatestore.go

+18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package mapper
33
import (
44
cyclopsv1alpha1 "github.com/cyclops-ui/cycops-ctrl/api/v1alpha1"
55
"github.com/cyclops-ui/cycops-ctrl/internal/models/dto"
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
67
)
78

89
func TemplateStoreListToDTO(store []cyclopsv1alpha1.TemplateStore) []dto.TemplateStore {
@@ -21,3 +22,20 @@ func TemplateStoreListToDTO(store []cyclopsv1alpha1.TemplateStore) []dto.Templat
2122

2223
return out
2324
}
25+
26+
func DTOToTemplateStoreList(store dto.TemplateStore) *cyclopsv1alpha1.TemplateStore {
27+
return &cyclopsv1alpha1.TemplateStore{
28+
TypeMeta: metav1.TypeMeta{
29+
Kind: "TemplateStore",
30+
APIVersion: "cyclops-ui.com/v1alpha1",
31+
},
32+
ObjectMeta: metav1.ObjectMeta{
33+
Name: store.Name,
34+
},
35+
Spec: cyclopsv1alpha1.TemplateRef{
36+
URL: store.TemplateRef.URL,
37+
Path: store.TemplateRef.Path,
38+
Version: store.TemplateRef.Version,
39+
},
40+
}
41+
}

cyclops-ctrl/internal/models/dto/modules.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ type Module struct {
1010
}
1111

1212
type Template struct {
13-
URL string `json:"repo"`
14-
Path string `json:"path"`
15-
Version string `json:"version"`
13+
URL string `json:"repo" binding:"required"`
14+
Path string `json:"path" binding:"required"`
15+
Version string `json:"version" binding:"required"`
1616
}
1717

1818
type TemplatesResponse struct {
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dto
22

33
type TemplateStore struct {
4-
Name string `json:"name"`
4+
Name string `json:"name" binding:"required"`
55
TemplateRef Template `json:"ref"`
66
}

cyclops-ui/src/components/layouts/Sidebar.tsx

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import React from "react";
22
import { Button, Menu, MenuProps } from "antd";
3-
import {AppstoreAddOutlined, HddOutlined, BugFilled, SnippetsOutlined} from "@ant-design/icons";
3+
import {
4+
AppstoreAddOutlined,
5+
HddOutlined,
6+
BugFilled,
7+
SnippetsOutlined,
8+
GithubFilled
9+
} from "@ant-design/icons";
410
import { useLocation } from "react-router";
511
import PathConstants from "../../routes/PathConstants";
612
import { Link } from "react-router-dom";
13+
import styles from "./styles.module.css"
714

815
const SideNav = () => {
916
const location = useLocation().pathname.split("/")[1];
@@ -26,6 +33,14 @@ const SideNav = () => {
2633
},
2734
];
2835

36+
const tagChangelogLink = (tag: string) => {
37+
if (tag === "v0.0.0") {
38+
return "https://github.com/cyclops-ui/cyclops/releases"
39+
}
40+
41+
return "https://github.com/cyclops-ui/cyclops/releases/tag/" + tag
42+
}
43+
2944
return (
3045
<div
3146
style={{ display: "flex", flexDirection: "column", minHeight: "100vh" }}
@@ -66,7 +81,9 @@ const SideNav = () => {
6681
margin: "25px",
6782
marginTop: "0",
6883
}}>
69-
{window.__RUNTIME_CONFIG__.REACT_APP_VERSION}
84+
<Link className={styles.taglink} to={tagChangelogLink(window.__RUNTIME_CONFIG__.REACT_APP_VERSION)}>
85+
<GithubFilled/> {window.__RUNTIME_CONFIG__.REACT_APP_VERSION}
86+
</Link>
7087
</center>
7188
</div>
7289
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.taglink {
2+
color: #FFF;
3+
}

cyclops-ui/src/components/pages/TemplateStore.tsx

+97-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import React, { useEffect, useState } from "react";
2-
import {Col, Table, Typography, Alert, Row} from "antd";
2+
import {Col, Table, Typography, Alert, Row, Button, Tabs, Modal, Form, Input} from "antd";
33
import axios from "axios";
44
import Title from "antd/es/typography/Title";
55

66
const TemplateStore = () => {
77
const [templates, setTemplates] = useState([]);
8-
const [error, setError] = useState({
8+
const [newTemplateModal, setNewTemplateModal] = useState(false)
9+
const [confirmLoading, setConfirmLoading] = useState(false);
10+
const [error, setError] = useState({
911
message: "",
1012
description: "",
1113
});
1214

13-
useEffect(() => {
15+
const [form] = Form.useForm();
16+
17+
useEffect(() => {
1418
axios
1519
.get(`/api/templates/store`)
1620
.then((res) => {
@@ -32,6 +36,41 @@ const TemplateStore = () => {
3236
});
3337
}, []);
3438

39+
const handleOK = () => {
40+
form.submit();
41+
}
42+
43+
const handleSubmit = (values: any) => {
44+
setConfirmLoading(true);
45+
46+
axios
47+
.put(`/api/templates/store`, values)
48+
.then((res) => {
49+
setNewTemplateModal(false);
50+
setConfirmLoading(true);
51+
window.location.href = "/templates";
52+
})
53+
.catch((error) => {
54+
if (error.response === undefined) {
55+
setError({
56+
message: String(error),
57+
description:
58+
"Check if Cyclops backend is available on: " +
59+
window.__RUNTIME_CONFIG__.REACT_APP_CYCLOPS_CTRL_HOST,
60+
});
61+
} else {
62+
setError({
63+
message: error.message,
64+
description: error.response.data,
65+
});
66+
}
67+
});
68+
}
69+
70+
const handleCancelModal = () => {
71+
setNewTemplateModal(false);
72+
}
73+
3574
return (
3675
<div>
3776
{error.message.length !== 0 && (
@@ -53,6 +92,15 @@ const TemplateStore = () => {
5392
<Col span={18}>
5493
<Title level={2}>Templates: {templates.length}</Title>
5594
</Col>
95+
<Col span={6}>
96+
<Button
97+
type={"primary"}
98+
block
99+
onClick={() => { setNewTemplateModal(true) }
100+
}>
101+
Add template reference
102+
</Button>
103+
</Col>
56104
</Row>
57105
<Col span={24} style={{ overflowX: "auto" }}>
58106
<Table dataSource={templates}>
@@ -72,6 +120,52 @@ const TemplateStore = () => {
72120
<Table.Column title="Version" dataIndex={["ref", "version"]} width={"10%"} />
73121
</Table>
74122
</Col>
123+
<Modal
124+
title="Add template ref"
125+
open={newTemplateModal}
126+
onOk={handleOK}
127+
onCancel={handleCancelModal}
128+
confirmLoading={confirmLoading}
129+
width={"60%"}
130+
>
131+
<Form
132+
onFinish={handleSubmit}
133+
form={form}
134+
initialValues={{ remember: true }}
135+
labelCol={{span: 6}}
136+
>
137+
<Form.Item
138+
label="Template ref name"
139+
name={"name"}
140+
rules={[{ required: true, message: 'Template ref is required' }]}
141+
>
142+
<Input />
143+
</Form.Item>
144+
145+
<Form.Item
146+
label="Repository URL"
147+
name={["ref", "repo"]}
148+
rules={[{ required: true, message: 'Repo URL is required' }]}
149+
>
150+
<Input />
151+
</Form.Item>
152+
153+
<Form.Item
154+
label="Path"
155+
name={["ref", "path"]}
156+
rules={[{ required: true, message: 'Path is required' }]}
157+
>
158+
<Input />
159+
</Form.Item>
160+
161+
<Form.Item
162+
label="Version"
163+
name={["ref", "version"]}
164+
>
165+
<Input />
166+
</Form.Item>
167+
</Form>
168+
</Modal>
75169
</div>
76170
);
77171
};

0 commit comments

Comments
 (0)