1
2
3
4
5
6 package services
7
8 import (
9 "context"
10 "net/http"
11
12 api "edge-infra.dev/pkg/edge/device-registrar/api/v1alpha1"
13 "edge-infra.dev/pkg/edge/device-registrar/config"
14 "edge-infra.dev/pkg/lib/crypto"
15
16 "github.com/gin-gonic/gin"
17 k8serrors "k8s.io/apimachinery/pkg/api/errors"
18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19 "k8s.io/apimachinery/pkg/types"
20
21 "sigs.k8s.io/controller-runtime/pkg/client"
22 )
23
24
25
26
27
28
29
30 type DeviceRegistrationResponse struct {
31
32
33
34 BootstrapURL string `json:"bootstrapURL"`
35
36
37
38 HostMapping HostMapping `json:"hostMapping"`
39 }
40
41
42
43 type ListApplicationsResponse struct {
44
45
46
47 ID string `json:"id"`
48
49
50
51 Name string `json:"name"`
52
53
54
55 Description string `json:"description"`
56 }
57
58
59 type HostMapping struct {
60
61
62
63 Host string `json:"host"`
64
65
66
67 VIP string `json:"vip"`
68 }
69
70
71
72
73
74
75
76
77
78 func ListApplications(c *gin.Context) {
79 k8sClient, ctx, cancel := config.GetClientandContext(c)
80 defer cancel()
81
82 externalApplications := &api.ExternalApplicationList{}
83 err := k8sClient.List(ctx, externalApplications, client.InNamespace(config.Namespace))
84 if err != nil {
85 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to retrieve applications: " + err.Error()})
86 return
87 }
88
89 listApplicationsResponse := []ListApplicationsResponse{}
90 for _, app := range externalApplications.Items {
91 listApplicationsResponse = append(listApplicationsResponse, ListApplicationsResponse{
92 ID: app.Spec.ID,
93 Name: app.GetName(),
94 Description: app.Spec.Description,
95 })
96 }
97
98 c.JSON(http.StatusOK, listApplicationsResponse)
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112 func RegisterDevice(c *gin.Context) {
113 req, err := checkRequest(c)
114 if err != nil {
115 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
116 return
117 }
118
119 k8sClient, ctx, cancel := config.GetClientandContext(c)
120 defer cancel()
121
122
123 device, err := createDevice(ctx, k8sClient, req)
124 if err != nil {
125 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create external device: " + err.Error()})
126 return
127 }
128
129
130 applicationSubjects, extApps, err := getAppSubjectsAndExternalApps(ctx, k8sClient, c, req)
131 if err != nil {
132 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get External Application: " + err.Error()})
133 return
134 }
135
136
137 deviceBindingName := device.GetName() + "-device-binding"
138 deviceBinding := &api.DeviceBinding{
139 ObjectMeta: metav1.ObjectMeta{
140 Name: deviceBindingName,
141 Namespace: device.GetNamespace(),
142 OwnerReferences: []metav1.OwnerReference{
143 {
144 APIVersion: api.GroupVersion.String(),
145 Kind: "ExternalDevice",
146 Name: device.GetName(),
147 UID: device.GetUID(),
148 },
149 },
150 },
151 Spec: api.DeviceBindingSpec{
152 Device: api.DeviceSubject{
153 APIGroup: api.GroupVersion.Group,
154 Kind: "ExternalDevice",
155 Name: device.GetName(),
156 },
157 Applications: applicationSubjects,
158 },
159 }
160
161 if err := k8sClient.Create(ctx, deviceBinding); err != nil && !k8serrors.IsAlreadyExists(err) {
162 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create device binding: " + err.Error()})
163 return
164 }
165
166
167 if err := k8sClient.Get(ctx, types.NamespacedName{
168 Name: deviceBindingName,
169 Namespace: device.GetNamespace(),
170 }, deviceBinding); err != nil {
171 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get device binding: " + err.Error()})
172 return
173 }
174
175
176 for _, extApp := range extApps {
177 client := createClient(device, extApp, deviceBinding)
178
179
180 client.Spec.SecretName = device.GetName() + "-" + extApp.GetName() + "-secret"
181 if err := k8sClient.Create(ctx, client); err != nil && !k8serrors.IsAlreadyExists(err) {
182 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create edgeID client: " + err.Error()})
183 return
184 }
185 }
186
187
188
189 ac, err := generateActivationCode()
190 if err != nil {
191 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create activation code: " + err.Error()})
192 return
193 }
194 deviceBinding.Status.ActivationCode = ac.Plain()
195 deviceBinding.Status.Timestamp = metav1.Now()
196
197 err = k8sClient.Status().Update(ctx, deviceBinding)
198 if err != nil {
199 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update deviceBinding status: " + err.Error()})
200 return
201 }
202
203 storeID := ""
204 if storeID, err = config.GetStoreID(ctx, k8sClient); err != nil {
205 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to retrieve edge-info: " + err.Error()})
206 return
207 }
208
209 err = k8sClient.Get(ctx, types.NamespacedName{
210 Name: device.GetName(),
211 Namespace: config.Namespace,
212 }, device)
213 if err != nil {
214 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to retrieve external device: " + err.Error()})
215 return
216 }
217
218 cert := createCert(device, storeID)
219 if err := k8sClient.Create(ctx, cert); err != nil {
220 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create client certificate: " + err.Error()})
221 return
222 }
223
224 vip, err := config.GetVIP(ctx, k8sClient)
225 if err != nil {
226 c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to retrieve VIP address: " + err.Error()})
227 return
228 }
229
230 resp := DeviceRegistrationResponse{
231 BootstrapURL: config.BootstrapURL + "/" + deviceBinding.Status.ActivationCode,
232 HostMapping: HostMapping{
233 Host: config.BootstrapHost,
234 VIP: vip,
235 },
236 }
237 c.JSON(http.StatusOK, resp)
238 }
239
240 func getExternalApplicationByID(ctx context.Context, c *gin.Context, k8sClient client.Client, appID string) (*api.ExternalApplication, error) {
241 applications := &api.ExternalApplicationList{}
242 err := k8sClient.List(ctx, applications, client.InNamespace(config.Namespace))
243 if err != nil {
244 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
245 return nil, err
246 }
247 for _, app := range applications.Items {
248 if app.Spec.ID == appID {
249
250 return &app, nil
251 }
252 }
253
254
255 return nil, k8serrors.NewNotFound(api.GroupVersion.WithResource("externalapplications").GroupResource(), appID)
256 }
257
258 func generateActivationCode() (crypto.Credential, error) {
259 ac, err := crypto.GenerateRandomActivationCode()
260 if err != nil {
261 return nil, err
262 }
263 return ac, nil
264 }
265
View as plain text