• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubevirt / kubevirt / 53e09dbe-7149-4eef-b52c-e1d16135149a

24 Feb 2026 10:58AM UTC coverage: 71.243% (+0.07%) from 71.17%
53e09dbe-7149-4eef-b52c-e1d16135149a

push

prow

web-flow
Merge pull request #16662 from mhenriks/containerpath-volumes

VEP 165: Containerpath Volumes

411 of 454 new or added lines in 14 files covered. (90.53%)

9 existing lines in 3 files now uncovered.

74412 of 104448 relevant lines covered (71.24%)

587.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.39
/pkg/virt-api/api.go
1
/*
2
 * This file is part of the KubeVirt project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 *
16
 * Copyright The KubeVirt Authors.
17
 *
18
 */
19

20
package virt_api
21

22
import (
23
        "context"
24
        "crypto/tls"
25
        "encoding/json"
26
        "fmt"
27
        "net/http"
28
        "os"
29
        "os/signal"
30
        "sync"
31
        "syscall"
32
        "time"
33

34
        builderv3 "k8s.io/kube-openapi/pkg/builder3"
35
        "k8s.io/kube-openapi/pkg/common/restfuladapter"
36
        "k8s.io/kube-openapi/pkg/validation/spec"
37

38
        kvtls "kubevirt.io/kubevirt/pkg/util/tls"
39

40
        restful "github.com/emicklei/go-restful/v3"
41
        "github.com/prometheus/client_golang/prometheus/promhttp"
42
        flag "github.com/spf13/pflag"
43
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
44
        "k8s.io/apimachinery/pkg/runtime/schema"
45
        "k8s.io/apimachinery/pkg/util/validation/field"
46
        "k8s.io/client-go/tools/cache"
47
        certificate2 "k8s.io/client-go/util/certificate"
48
        "k8s.io/client-go/util/flowcontrol"
49
        aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
50

51
        "kubevirt.io/kubevirt/pkg/util/ratelimiter"
52

53
        backupv1 "kubevirt.io/api/backup/v1alpha1"
54
        v1 "kubevirt.io/api/core/v1"
55
        "kubevirt.io/client-go/kubecli"
56
        "kubevirt.io/client-go/log"
57
        clientutil "kubevirt.io/client-go/util"
58
        virtversion "kubevirt.io/client-go/version"
59

60
        v12 "kubevirt.io/client-go/api"
61

62
        "kubevirt.io/kubevirt/pkg/certificates/bootstrap"
63
        "kubevirt.io/kubevirt/pkg/controller"
64
        "kubevirt.io/kubevirt/pkg/healthz"
65
        clientmetrics "kubevirt.io/kubevirt/pkg/monitoring/metrics/common/client"
66
        metrics "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-api"
67
        "kubevirt.io/kubevirt/pkg/monitoring/profiler"
68
        netadmitter "kubevirt.io/kubevirt/pkg/network/admitter"
69
        mime "kubevirt.io/kubevirt/pkg/rest"
70
        "kubevirt.io/kubevirt/pkg/rest/filter"
71
        "kubevirt.io/kubevirt/pkg/service"
72
        "kubevirt.io/kubevirt/pkg/util"
73
        "kubevirt.io/kubevirt/pkg/util/openapi"
74
        "kubevirt.io/kubevirt/pkg/virt-api/definitions"
75
        "kubevirt.io/kubevirt/pkg/virt-api/rest"
76
        "kubevirt.io/kubevirt/pkg/virt-api/webhooks"
77
        mutating_webhook "kubevirt.io/kubevirt/pkg/virt-api/webhooks/mutating-webhook"
78
        validating_webhook "kubevirt.io/kubevirt/pkg/virt-api/webhooks/validating-webhook"
79
        virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
80
        "kubevirt.io/kubevirt/pkg/virt-operator/resource/generate/components"
81
        virtoperatorutils "kubevirt.io/kubevirt/pkg/virt-operator/util"
82
)
83

84
const (
85
        // Default port that virt-api listens on.
86
        defaultPort = 443
87

88
        // Default address that virt-api listens on.
89
        defaultHost = "0.0.0.0"
90

91
        DefaultConsoleServerPort = 8186
92

93
        defaultCAConfigMapName     = "kubevirt-ca"
94
        defaultTlsCertFilePath     = "/etc/virt-api/certificates/tls.crt"
95
        defaultTlsKeyFilePath      = "/etc/virt-api/certificates/tls.key"
96
        defaultHandlerCertFilePath = "/etc/virt-handler/clientcertificates/tls.crt"
97
        defaultHandlerKeyFilePath  = "/etc/virt-handler/clientcertificates/tls.key"
98

99
        httpStatusNotFoundMessage     = "Not Found"
100
        httpStatusBadRequestMessage   = "Bad Request"
101
        httpStatusInternalServerError = "Internal Server Error"
102
)
103

104
type VirtApi interface {
105
        Compose()
106
        Run()
107
        AddFlags()
108
        ConfigureOpenAPIService()
109
        Execute()
110
}
111

112
type virtAPIApp struct {
113
        service.ServiceListen
114
        SwaggerUI        string
115
        SubresourcesOnly bool
116
        virtCli          kubecli.KubevirtClient
117
        aggregatorClient *aggregatorclient.Clientset
118
        authorizor       rest.VirtApiAuthorizor
119
        certsDirectory   string
120
        clusterConfig    *virtconfig.ClusterConfig
121

122
        namespace               string
123
        host                    string
124
        tlsConfig               *tls.Config
125
        consoleServerPort       int
126
        certmanager             certificate2.Manager
127
        handlerTLSConfiguration *tls.Config
128
        handlerCertManager      certificate2.Manager
129

130
        caConfigMapName              string
131
        tlsCertFilePath              string
132
        tlsKeyFilePath               string
133
        handlerCertFilePath          string
134
        handlerKeyFilePath           string
135
        externallyManaged            bool
136
        reloadableRateLimiter        *ratelimiter.ReloadableRateLimiter
137
        reloadableWebhookRateLimiter *ratelimiter.ReloadableRateLimiter
138

139
        // indicates if controllers were started with or without CDI/DataSource support
140
        hasCDIDataSource bool
141
        // the channel used to trigger re-initialization.
142
        reInitChan chan string
143

144
        kubeVirtServiceAccounts map[string]struct{}
145
}
146

147
var (
148
        _                service.Service = &virtAPIApp{}
149
        apiHealthVersion                 = new(healthz.KubeApiHealthzVersion)
150
)
151

152
func NewVirtApi() VirtApi {
×
153

×
154
        app := &virtAPIApp{}
×
155
        app.BindAddress = defaultHost
×
156
        app.Port = defaultPort
×
157

×
158
        return app
×
159
}
×
160

161
func (app *virtAPIApp) Execute() {
×
162
        if err := metrics.SetupMetrics(); err != nil {
×
163
                panic(err)
×
164
        }
165

166
        app.reloadableRateLimiter = ratelimiter.NewReloadableRateLimiter(flowcontrol.NewTokenBucketRateLimiter(virtconfig.DefaultVirtAPIQPS, virtconfig.DefaultVirtAPIBurst))
×
167
        app.reloadableWebhookRateLimiter = ratelimiter.NewReloadableRateLimiter(flowcontrol.NewTokenBucketRateLimiter(virtconfig.DefaultVirtWebhookClientQPS, virtconfig.DefaultVirtWebhookClientBurst))
×
168

×
169
        clientmetrics.RegisterRestConfigHooks()
×
170
        clientConfig, err := kubecli.GetKubevirtClientConfig()
×
171
        if err != nil {
×
172
                panic(err)
×
173
        }
174
        clientConfig.RateLimiter = app.reloadableRateLimiter
×
175
        app.virtCli, err = kubecli.GetKubevirtClientFromRESTConfig(clientConfig)
×
176
        if err != nil {
×
177
                panic(err)
×
178
        }
179

180
        authorizor, err := rest.NewAuthorizor(app.reloadableWebhookRateLimiter)
×
181
        if err != nil {
×
182
                panic(err)
×
183
        }
184

185
        app.aggregatorClient = aggregatorclient.NewForConfigOrDie(clientConfig)
×
186

×
187
        app.authorizor = authorizor
×
188

×
189
        app.certsDirectory, err = os.MkdirTemp("", "certsdir")
×
190
        if err != nil {
×
191
                panic(err)
×
192
        }
193
        app.namespace, err = clientutil.GetNamespace()
×
194
        if err != nil {
×
195
                panic(err)
×
196
        }
197

198
        app.kubeVirtServiceAccounts = webhooks.KubeVirtServiceAccounts(app.namespace)
×
199

×
200
        app.ConfigureOpenAPIService()
×
201
        app.reInitChan = make(chan string, 10)
×
202

×
203
        app.Run()
×
204
}
205

206
func subresourceAPIGroup() metav1.APIGroup {
2✔
207
        apiGroup := metav1.APIGroup{
2✔
208
                Name: "subresource.kubevirt.io",
2✔
209
                PreferredVersion: metav1.GroupVersionForDiscovery{
2✔
210
                        GroupVersion: v1.SubresourceGroupVersions[0].Group + "/" + v1.SubresourceGroupVersions[0].Version,
2✔
211
                        Version:      v1.SubresourceGroupVersions[0].Version,
2✔
212
                },
2✔
213
        }
2✔
214

2✔
215
        for _, version := range v1.SubresourceGroupVersions {
6✔
216
                apiGroup.Versions = append(apiGroup.Versions, metav1.GroupVersionForDiscovery{
4✔
217
                        GroupVersion: version.Group + "/" + version.Version,
4✔
218
                        Version:      version.Version,
4✔
219
                })
4✔
220
        }
4✔
221
        apiGroup.ServerAddressByClientCIDRs = append(apiGroup.ServerAddressByClientCIDRs, metav1.ServerAddressByClientCIDR{
2✔
222
                ClientCIDR:    "0.0.0.0/0",
2✔
223
                ServerAddress: "",
2✔
224
        })
2✔
225
        apiGroup.Kind = "APIGroup"
2✔
226
        return apiGroup
2✔
227
}
228

229
func (app *virtAPIApp) composeSubresources() {
7✔
230

7✔
231
        var subwss []*restful.WebService
7✔
232

7✔
233
        for _, version := range v1.SubresourceGroupVersions {
21✔
234
                subresourcesvmGVR := schema.GroupVersionResource{Group: version.Group, Version: version.Version, Resource: "virtualmachines"}
14✔
235
                subresourcesvmiGVR := schema.GroupVersionResource{Group: version.Group, Version: version.Version, Resource: "virtualmachineinstances"}
14✔
236
                expandvmspecGVR := schema.GroupVersionResource{Group: version.Group, Version: version.Version, Resource: "expand-vm-spec"}
14✔
237

14✔
238
                subws := new(restful.WebService)
14✔
239
                subws.Doc(fmt.Sprintf("KubeVirt \"%s\" Subresource API.", version.Version))
14✔
240
                subws.Path(definitions.GroupVersionBasePath(version))
14✔
241

14✔
242
                subresourceApp := rest.NewSubresourceAPIApp(app.virtCli, app.consoleServerPort, app.handlerTLSConfiguration, app.clusterConfig)
14✔
243

14✔
244
                restartRouteBuilder := subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("restart")).
14✔
245
                        To(subresourceApp.RestartVMRequestHandler).
14✔
246
                        Consumes(mime.MIME_ANY).
14✔
247
                        Reads(v1.RestartOptions{}).
14✔
248
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
249
                        Operation(version.Version+"Restart").
14✔
250
                        Doc("Restart a VirtualMachine object.").
14✔
251
                        Returns(http.StatusOK, "OK", "").
14✔
252
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
253
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "")
14✔
254
                restartRouteBuilder.ParameterNamed("body").Required(false)
14✔
255
                subws.Route(restartRouteBuilder)
14✔
256

14✔
257
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("migrate")).
14✔
258
                        To(subresourceApp.MigrateVMRequestHandler).
14✔
259
                        Consumes(mime.MIME_ANY).
14✔
260
                        Reads(v1.MigrateOptions{}).
14✔
261
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
262
                        Operation(version.Version+"Migrate").
14✔
263
                        Doc("Migrate a running VirtualMachine to another node.").
14✔
264
                        Returns(http.StatusOK, "OK", "").
14✔
265
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
266
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
267

14✔
268
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("start")).
14✔
269
                        To(subresourceApp.StartVMRequestHandler).
14✔
270
                        Consumes(mime.MIME_ANY).
14✔
271
                        Reads(v1.StartOptions{}).
14✔
272
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
273
                        Operation(version.Version+"Start").
14✔
274
                        Doc("Start a VirtualMachine object.").
14✔
275
                        Returns(http.StatusOK, "OK", "").
14✔
276
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
277
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
278

14✔
279
                stopRouteBuilder := subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("stop")).
14✔
280
                        To(subresourceApp.StopVMRequestHandler).
14✔
281
                        Consumes(mime.MIME_ANY).
14✔
282
                        Reads(v1.StopOptions{}).
14✔
283
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
284
                        Operation(version.Version+"Stop").
14✔
285
                        Doc("Stop a VirtualMachine object.").
14✔
286
                        Returns(http.StatusOK, "OK", "").
14✔
287
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
288
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "")
14✔
289
                stopRouteBuilder.ParameterNamed("body").Required(false)
14✔
290
                subws.Route(stopRouteBuilder)
14✔
291

14✔
292
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("expand-spec")).
14✔
293
                        To(subresourceApp.ExpandSpecVMRequestHandler).
14✔
294
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
295
                        Operation(version.Version+"vm-ExpandSpec").
14✔
296
                        Produces(restful.MIME_JSON).
14✔
297
                        Doc("Get VirtualMachine object with expanded instancetype and preference.").
14✔
298
                        Returns(http.StatusOK, "OK", "").
14✔
299
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
300
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
301

14✔
302
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("freeze")).
14✔
303
                        To(subresourceApp.FreezeVMIRequestHandler).
14✔
304
                        Consumes(mime.MIME_ANY).
14✔
305
                        Reads(v1.FreezeUnfreezeTimeout{}).
14✔
306
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
307
                        Operation(version.Version+"Freeze").
14✔
308
                        Doc("Freeze a VirtualMachineInstance object.").
14✔
309
                        Returns(http.StatusOK, "OK", "").
14✔
310
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
311

14✔
312
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("unfreeze")).
14✔
313
                        To(subresourceApp.UnfreezeVMIRequestHandler).
14✔
314
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
315
                        Operation(version.Version+"Unfreeze").
14✔
316
                        Doc("Unfreeze a VirtualMachineInstance object.").
14✔
317
                        Returns(http.StatusOK, "OK", "").
14✔
318
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
319

14✔
320
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("reset")).
14✔
321
                        To(subresourceApp.ResetVMIRequestHandler).
14✔
322
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
323
                        Operation(version.Version+"Reset").
14✔
324
                        Doc("Reset a VirtualMachineInstance object.").
14✔
325
                        Returns(http.StatusOK, "OK", "").
14✔
326
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
327

14✔
328
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("softreboot")).
14✔
329
                        To(subresourceApp.SoftRebootVMIRequestHandler).
14✔
330
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
331
                        Operation(version.Version+"SoftReboot").
14✔
332
                        Doc("Soft reboot a VirtualMachineInstance object.").
14✔
333
                        Returns(http.StatusOK, "OK", "").
14✔
334
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
335

14✔
336
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("pause")).
14✔
337
                        To(subresourceApp.PauseVMIRequestHandler).
14✔
338
                        Consumes(mime.MIME_ANY).
14✔
339
                        Reads(v1.PauseOptions{}).
14✔
340
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
341
                        Operation(version.Version+"Pause").
14✔
342
                        Doc("Pause a VirtualMachineInstance object.").
14✔
343
                        Returns(http.StatusOK, "OK", "").
14✔
344
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
345
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
346

14✔
347
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("unpause")).
14✔
348
                        To(subresourceApp.UnpauseVMIRequestHandler). // handles VMIs as well
14✔
349
                        Consumes(mime.MIME_ANY).
14✔
350
                        Reads(v1.UnpauseOptions{}).
14✔
351
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
352
                        Operation(version.Version+"Unpause").
14✔
353
                        Doc("Unpause a VirtualMachineInstance object.").
14✔
354
                        Returns(http.StatusOK, "OK", "").
14✔
355
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
356
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
357

14✔
358
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("console")).
14✔
359
                        To(subresourceApp.ConsoleRequestHandler).
14✔
360
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
361
                        Operation(version.Version + "Console").
14✔
362
                        Doc("Open a websocket connection to a serial console on the specified VirtualMachineInstance."))
14✔
363

14✔
364
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("vnc")).
14✔
365
                        To(subresourceApp.VNCRequestHandler).
14✔
366
                        Param(definitions.NamespaceParam(subws)).
14✔
367
                        Param(definitions.NameParam(subws)).
14✔
368
                        Param(definitions.PreserveSessionParam(subws)).
14✔
369
                        Operation(version.Version + "VNC").
14✔
370
                        Doc("Open a websocket connection to connect to VNC on the specified VirtualMachineInstance."))
14✔
371
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("vnc/screenshot")).
14✔
372
                        To(subresourceApp.ScreenshotRequestHandler).
14✔
373
                        Param(definitions.NamespaceParam(subws)).
14✔
374
                        Param(definitions.NameParam(subws)).
14✔
375
                        Param(definitions.MoveCursorParam(subws)).
14✔
376
                        Operation(version.Version + "VNCScreenshot").
14✔
377
                        Doc("Get a PNG VNC screenshot of the specified VirtualMachineInstance."))
14✔
378
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("usbredir")).
14✔
379
                        To(subresourceApp.USBRedirRequestHandler).
14✔
380
                        Param(definitions.NamespaceParam(subws)).
14✔
381
                        Param(definitions.NameParam(subws)).
14✔
382
                        Operation(version.Version + "usbredir").
14✔
383
                        Doc("Open a websocket connection to connect to USB device on the specified VirtualMachineInstance."))
14✔
384

14✔
385
                // VMI endpoint
14✔
386
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("portforward") + definitions.PortPath).
14✔
387
                        To(subresourceApp.PortForwardRequestHandler(subresourceApp.FetchVirtualMachineInstance)).
14✔
388
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
389
                        Param(definitions.PortForwardPortParameter(subws)).
14✔
390
                        Operation(version.Version + "vmi-PortForward").
14✔
391
                        Doc("Open a websocket connection forwarding traffic to the specified VirtualMachineInstance and port."))
14✔
392
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("portforward") + definitions.PortPath + definitions.ProtocolPath).
14✔
393
                        To(subresourceApp.PortForwardRequestHandler(subresourceApp.FetchVirtualMachineInstance)).
14✔
394
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
395
                        Param(definitions.PortForwardPortParameter(subws)).
14✔
396
                        Param(definitions.PortForwardProtocolParameter(subws)).
14✔
397
                        Operation(version.Version + "vmi-PortForwardWithProtocol").
14✔
398
                        Doc("Open a websocket connection forwarding traffic of the specified protocol (either tcp or udp) to the specified VirtualMachineInstance and port."))
14✔
399
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("vsock")).
14✔
400
                        To(subresourceApp.VSOCKRequestHandler).
14✔
401
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).Param(definitions.VSOCKPortParameter(subws)).Param(definitions.VSOCKTLSParameter(subws)).
14✔
402
                        Operation(version.Version + "VSOCK").
14✔
403
                        Doc("Open a websocket connection forwarding traffic to the specified VirtualMachineInstance and port via VSOCK."))
14✔
404

14✔
405
                // VM endpoint
14✔
406
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmGVR) + definitions.SubResourcePath("portforward") + definitions.PortPath).
14✔
407
                        To(subresourceApp.PortForwardRequestHandler(subresourceApp.FetchVirtualMachineInstanceForVM)).
14✔
408
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
409
                        Param(definitions.PortForwardPortParameter(subws)).
14✔
410
                        Operation(version.Version + "vm-PortForward").
14✔
411
                        Doc("Open a websocket connection forwarding traffic to the running VMI for the specified VirtualMachine and port."))
14✔
412
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmGVR) + definitions.SubResourcePath("portforward") + definitions.PortPath + definitions.ProtocolPath).
14✔
413
                        To(subresourceApp.PortForwardRequestHandler(subresourceApp.FetchVirtualMachineInstanceForVM)).
14✔
414
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
415
                        Param(definitions.PortForwardPortParameter(subws)).
14✔
416
                        Param(definitions.PortForwardProtocolParameter(subws)).
14✔
417
                        Operation(version.Version + "vm-PortForwardWithProtocol").
14✔
418
                        Doc("Open a websocket connection forwarding traffic of the specified protocol (either tcp or udp) to the specified VirtualMachine and port."))
14✔
419

14✔
420
                subws.Route(subws.PUT(definitions.NamespacedResourceBasePath(expandvmspecGVR)).
14✔
421
                        To(subresourceApp.ExpandSpecRequestHandler).
14✔
422
                        Param(definitions.NamespaceParam(subws)).
14✔
423
                        Operation(version.Version+"ExpandSpec").
14✔
424
                        Consumes(restful.MIME_JSON).
14✔
425
                        Produces(restful.MIME_JSON).
14✔
426
                        Doc("Expands instancetype and preference into the passed VirtualMachine object.").
14✔
427
                        Returns(http.StatusOK, "OK", "").
14✔
428
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "").
14✔
429
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
430

14✔
431
                subws.Route(subws.GET(definitions.SubResourcePath("version")).Produces(restful.MIME_JSON).
14✔
432
                        To(func(request *restful.Request, response *restful.Response) {
15✔
433
                                response.WriteAsJson(virtversion.Get())
1✔
434
                        }).Operation(version.Version + "Version"))
1✔
435

436
                subws.Route(subws.GET(definitions.SubResourcePath("start-cluster-profiler")).Produces(restful.MIME_JSON).
14✔
437
                        To(subresourceApp.StartClusterProfilerHandler).
14✔
438
                        Operation(version.Version + "start-cluster-profiler"))
14✔
439

14✔
440
                subws.Route(subws.GET(definitions.SubResourcePath("stop-cluster-profiler")).Produces(restful.MIME_JSON).
14✔
441
                        To(subresourceApp.StopClusterProfilerHandler).
14✔
442
                        Operation(version.Version + "stop-cluster-profiler"))
14✔
443

14✔
444
                subws.Route(subws.GET(definitions.SubResourcePath("dump-cluster-profiler")).Produces(restful.MIME_JSON).
14✔
445
                        To(subresourceApp.DumpClusterProfilerHandler).
14✔
446
                        Operation(version.Version + "dump-cluster-profiler"))
14✔
447

14✔
448
                subws.Route(subws.GET(definitions.SubResourcePath("guestfs")).Produces(restful.MIME_JSON).
14✔
449
                        To(app.GetGsInfo()).
14✔
450
                        Operation(version.Version+"Guestfs").
14✔
451
                        Returns(http.StatusOK, "OK", "").
14✔
452
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
453
                subws.Route(subws.GET(definitions.SubResourcePath("healthz")).
14✔
454
                        To(healthz.KubeConnectionHealthzFuncFactory(app.clusterConfig, apiHealthVersion)).
14✔
455
                        Consumes(restful.MIME_JSON).
14✔
456
                        Produces(restful.MIME_JSON).
14✔
457
                        Operation(version.Version+"CheckHealth").
14✔
458
                        Doc("Health endpoint").
14✔
459
                        Returns(http.StatusOK, "OK", "").
14✔
460
                        Returns(http.StatusInternalServerError, "Unhealthy", ""))
14✔
461
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("guestosinfo")).
14✔
462
                        To(subresourceApp.GuestOSInfo).
14✔
463
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
464
                        Consumes(restful.MIME_JSON).
14✔
465
                        Produces(restful.MIME_JSON).
14✔
466
                        Operation(version.Version+"Guestosinfo").
14✔
467
                        Doc("Get guest agent os information").
14✔
468
                        Writes(v1.VirtualMachineInstanceGuestAgentInfo{}).
14✔
469
                        Returns(http.StatusOK, "OK", v1.VirtualMachineInstanceGuestAgentInfo{}))
14✔
470

14✔
471
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("userlist")).
14✔
472
                        To(subresourceApp.UserList).
14✔
473
                        Consumes(restful.MIME_JSON).
14✔
474
                        Produces(restful.MIME_JSON).
14✔
475
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
476
                        Operation(version.Version+"Userlist").
14✔
477
                        Doc("Get list of active users via guest agent").
14✔
478
                        Writes(v1.VirtualMachineInstanceGuestOSUserList{}).
14✔
479
                        Returns(http.StatusOK, "OK", v1.VirtualMachineInstanceGuestOSUserList{}))
14✔
480

14✔
481
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("filesystemlist")).
14✔
482
                        To(subresourceApp.FilesystemList).
14✔
483
                        Consumes(restful.MIME_JSON).
14✔
484
                        Produces(restful.MIME_JSON).
14✔
485
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
486
                        Operation(version.Version+"Filesystemlist").
14✔
487
                        Doc("Get list of active filesystems on guest machine via guest agent").
14✔
488
                        Writes(v1.VirtualMachineInstanceFileSystemList{}).
14✔
489
                        Returns(http.StatusOK, "OK", v1.VirtualMachineInstanceFileSystemList{}))
14✔
490

14✔
491
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("objectgraph")).
14✔
492
                        To(subresourceApp.VMIObjectGraph).
14✔
493
                        Consumes(restful.MIME_JSON).
14✔
494
                        Reads(v1.ObjectGraphOptions{}).
14✔
495
                        Produces(restful.MIME_JSON).
14✔
496
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
497
                        Operation(version.Version+"vmi-objectgraph").
14✔
498
                        Doc("Get graph of objects related to a Virtual Machine Instance").
14✔
499
                        Writes(v1.ObjectGraphNode{}).
14✔
500
                        Returns(http.StatusOK, "OK", v1.ObjectGraphNode{}))
14✔
501

14✔
502
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("objectgraph")).
14✔
503
                        To(subresourceApp.VMObjectGraph).
14✔
504
                        Consumes(restful.MIME_JSON).
14✔
505
                        Reads(v1.ObjectGraphOptions{}).
14✔
506
                        Produces(restful.MIME_JSON).
14✔
507
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
508
                        Operation(version.Version+"vm-objectgraph").
14✔
509
                        Doc("Get graph of objects related to a Virtual Machine").
14✔
510
                        Writes(v1.ObjectGraphNode{}).
14✔
511
                        Returns(http.StatusOK, "OK", v1.ObjectGraphNode{}))
14✔
512

14✔
513
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("addvolume")).
14✔
514
                        To(subresourceApp.VMIAddVolumeRequestHandler).
14✔
515
                        Consumes(mime.MIME_ANY).
14✔
516
                        Reads(v1.AddVolumeOptions{}).
14✔
517
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
518
                        Operation(version.Version+"vmi-addvolume").
14✔
519
                        Doc("Add a volume and disk to a running Virtual Machine Instance").
14✔
520
                        Returns(http.StatusOK, "OK", "").
14✔
521
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
522

14✔
523
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("removevolume")).
14✔
524
                        To(subresourceApp.VMIRemoveVolumeRequestHandler).
14✔
525
                        Consumes(mime.MIME_ANY).
14✔
526
                        Reads(v1.RemoveVolumeOptions{}).
14✔
527
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
528
                        Operation(version.Version+"vmi-removevolume").
14✔
529
                        Doc("Removes a volume and disk from a running Virtual Machine Instance").
14✔
530
                        Returns(http.StatusOK, "OK", "").
14✔
531
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
532

14✔
533
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("addvolume")).
14✔
534
                        To(subresourceApp.VMAddVolumeRequestHandler).
14✔
535
                        Consumes(mime.MIME_ANY).
14✔
536
                        Reads(v1.AddVolumeOptions{}).
14✔
537
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
538
                        Operation(version.Version+"vm-addvolume").
14✔
539
                        Doc("Add a volume and disk to a running Virtual Machine.").
14✔
540
                        Returns(http.StatusOK, "OK", "").
14✔
541
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
542

14✔
543
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("removevolume")).
14✔
544
                        To(subresourceApp.VMRemoveVolumeRequestHandler).
14✔
545
                        Consumes(mime.MIME_ANY).
14✔
546
                        Reads(v1.RemoveVolumeOptions{}).
14✔
547
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
548
                        Operation(version.Version+"vm-removevolume").
14✔
549
                        Doc("Removes a volume and disk from a running Virtual Machine.").
14✔
550
                        Returns(http.StatusOK, "OK", "").
14✔
551
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
552

14✔
553
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("memorydump")).
14✔
554
                        To(subresourceApp.MemoryDumpVMRequestHandler).
14✔
555
                        Consumes(mime.MIME_ANY).
14✔
556
                        Reads(v1.VirtualMachineMemoryDumpRequest{}).
14✔
557
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
558
                        Operation(version.Version+"MemoryDump").
14✔
559
                        Doc("Dumps a VirtualMachineInstance memory.").
14✔
560
                        Returns(http.StatusOK, "OK", "").
14✔
561
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
562

14✔
563
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("removememorydump")).
14✔
564
                        To(subresourceApp.RemoveMemoryDumpVMRequestHandler).
14✔
565
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
566
                        Operation(version.Version+"RemoveMemoryDump").
14✔
567
                        Doc("Remove memory dump association.").
14✔
568
                        Returns(http.StatusOK, "OK", "").
14✔
569
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
570

14✔
571
                // AMD SEV endpoints
14✔
572
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("sev/fetchcertchain")).
14✔
573
                        To(subresourceApp.SEVFetchCertChainRequestHandler).
14✔
574
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
575
                        Consumes(restful.MIME_JSON).
14✔
576
                        Produces(restful.MIME_JSON).
14✔
577
                        Operation(version.Version+"SEVFetchCertChain").
14✔
578
                        Doc("Fetch SEV certificate chain from the node where Virtual Machine is scheduled").
14✔
579
                        Writes(v1.SEVPlatformInfo{}).
14✔
580
                        Returns(http.StatusOK, "OK", v1.SEVPlatformInfo{}))
14✔
581

14✔
582
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("sev/querylaunchmeasurement")).
14✔
583
                        To(subresourceApp.SEVQueryLaunchMeasurementHandler).
14✔
584
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
585
                        Consumes(restful.MIME_JSON).
14✔
586
                        Produces(restful.MIME_JSON).
14✔
587
                        Operation(version.Version+"SEVQueryLaunchMeasurement").
14✔
588
                        Doc("Query SEV launch measurement from a Virtual Machine").
14✔
589
                        Writes(v1.SEVMeasurementInfo{}).
14✔
590
                        Returns(http.StatusOK, "OK", v1.SEVMeasurementInfo{}))
14✔
591

14✔
592
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("sev/setupsession")).
14✔
593
                        To(subresourceApp.SEVSetupSessionHandler).
14✔
594
                        Consumes(mime.MIME_ANY).
14✔
595
                        Reads(v1.SEVSessionOptions{}).
14✔
596
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
597
                        Operation(version.Version+"SEVSetupSession").
14✔
598
                        Doc("Setup SEV session parameters for a Virtual Machine").
14✔
599
                        Returns(http.StatusOK, "OK", "").
14✔
600
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
601

14✔
602
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("sev/injectlaunchsecret")).
14✔
603
                        To(subresourceApp.SEVInjectLaunchSecretHandler).
14✔
604
                        Consumes(mime.MIME_ANY).
14✔
605
                        Reads(v1.SEVSecretOptions{}).
14✔
606
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
607
                        Operation(version.Version+"SEVInjectLaunchSecret").
14✔
608
                        Doc("Inject SEV launch secret into a Virtual Machine").
14✔
609
                        Returns(http.StatusOK, "OK", "").
14✔
610
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
611

14✔
612
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("evacuate/cancel")).
14✔
613
                        To(subresourceApp.EvacuateCancelHandler(subresourceApp.FetchVirtualMachineInstanceForVM)).
14✔
614
                        Consumes(mime.MIME_ANY).
14✔
615
                        Reads(v1.EvacuateCancelOptions{}).
14✔
616
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
617
                        Operation(version.Version+"vm-evacuatecancel").
14✔
618
                        Doc("Cancel evacuation Virtual Machine").
14✔
619
                        Returns(http.StatusOK, "OK", "").
14✔
620
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
621
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "").
14✔
622
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
623

14✔
624
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("evacuate/cancel")).
14✔
625
                        To(subresourceApp.EvacuateCancelHandler(subresourceApp.FetchVirtualMachineInstance)).
14✔
626
                        Consumes(mime.MIME_ANY).
14✔
627
                        Reads(v1.EvacuateCancelOptions{}).
14✔
628
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
629
                        Operation(version.Version+"vmi-evacuatecancel").
14✔
630
                        Doc("Cancel evacuation Virtual Machine Instance").
14✔
631
                        Returns(http.StatusOK, "OK", "").
14✔
632
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
633
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "").
14✔
634
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
14✔
635

14✔
636
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("backup")).
14✔
637
                        To(subresourceApp.BackupVMIRequestHandler).
14✔
638
                        Consumes(mime.MIME_ANY).
14✔
639
                        Reads(backupv1.BackupOptions{}).
14✔
640
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
641
                        Operation(version.Version+"Backup").
14✔
642
                        Doc("Initiate a VirtualMachineInstance backup.").
14✔
643
                        Returns(http.StatusOK, "OK", "").
14✔
644
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
645
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
646

14✔
647
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("redefine-checkpoint")).
14✔
648
                        To(subresourceApp.RedefineCheckpointVMIRequestHandler).
14✔
649
                        Consumes(mime.MIME_ANY).
14✔
650
                        Reads(backupv1.BackupCheckpoint{}).
14✔
651
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
14✔
652
                        Operation(version.Version+"RedefineCheckpoint").
14✔
653
                        Doc("Redefine a checkpoint for a VirtualMachineInstance.").
14✔
654
                        Returns(http.StatusOK, "OK", "").
14✔
655
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
14✔
656
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
14✔
657

14✔
658
                // Return empty api resource list.
14✔
659
                // K8s expects to be able to retrieve a resource list for each aggregated
14✔
660
                // app in order to discover what resources it provides. Without returning
14✔
661
                // an empty list here, there's a bug in the k8s resource discovery that
14✔
662
                // breaks kubectl's ability to reference short names for resources.
14✔
663
                subws.Route(subws.GET("/").
14✔
664
                        Produces(restful.MIME_JSON).Writes(metav1.APIResourceList{}).
14✔
665
                        To(func(request *restful.Request, response *restful.Response) {
15✔
666
                                list := &metav1.APIResourceList{}
1✔
667

1✔
668
                                list.Kind = "APIResourceList"
1✔
669
                                list.GroupVersion = version.Group + "/" + version.Version
1✔
670
                                list.APIVersion = "v1"
1✔
671
                                list.APIResources = []metav1.APIResource{
1✔
672
                                        {
1✔
673
                                                Name:       "expand-vm-spec",
1✔
674
                                                Namespaced: true,
1✔
675
                                        },
1✔
676
                                        {
1✔
677
                                                Name:       "virtualmachineinstances/vnc",
1✔
678
                                                Namespaced: true,
1✔
679
                                        },
1✔
680
                                        {
1✔
681
                                                Name:       "virtualmachineinstances/vnc/screenshot",
1✔
682
                                                Namespaced: true,
1✔
683
                                        },
1✔
684
                                        {
1✔
685
                                                Name:       "virtualmachineinstances/console",
1✔
686
                                                Namespaced: true,
1✔
687
                                        },
1✔
688
                                        {
1✔
689
                                                Name:       "virtualmachineinstances/portforward",
1✔
690
                                                Namespaced: true,
1✔
691
                                        },
1✔
692
                                        {
1✔
693
                                                Name:       "virtualmachineinstances/backup",
1✔
694
                                                Namespaced: true,
1✔
695
                                        },
1✔
696
                                        {
1✔
697
                                                Name:       "virtualmachineinstances/redefine-checkpoint",
1✔
698
                                                Namespaced: true,
1✔
699
                                        },
1✔
700
                                        {
1✔
701
                                                Name:       "virtualmachineinstances/pause",
1✔
702
                                                Namespaced: true,
1✔
703
                                        },
1✔
704
                                        {
1✔
705
                                                Name:       "virtualmachineinstances/unpause",
1✔
706
                                                Namespaced: true,
1✔
707
                                        },
1✔
708
                                        {
1✔
709
                                                Name:       "virtualmachineinstances/freeze",
1✔
710
                                                Namespaced: true,
1✔
711
                                        },
1✔
712
                                        {
1✔
713
                                                Name:       "virtualmachineinstances/unfreeze",
1✔
714
                                                Namespaced: true,
1✔
715
                                        },
1✔
716
                                        {
1✔
717
                                                Name:       "virtualmachineinstances/reset",
1✔
718
                                                Namespaced: true,
1✔
719
                                        },
1✔
720
                                        {
1✔
721
                                                Name:       "virtualmachineinstances/softreboot",
1✔
722
                                                Namespaced: true,
1✔
723
                                        },
1✔
724
                                        {
1✔
725
                                                Name:       "virtualmachines/start",
1✔
726
                                                Namespaced: true,
1✔
727
                                        },
1✔
728
                                        {
1✔
729
                                                Name:       "virtualmachines/stop",
1✔
730
                                                Namespaced: true,
1✔
731
                                        },
1✔
732
                                        {
1✔
733
                                                Name:       "virtualmachines/restart",
1✔
734
                                                Namespaced: true,
1✔
735
                                        },
1✔
736
                                        {
1✔
737
                                                Name:       "virtualmachines/migrate",
1✔
738
                                                Namespaced: true,
1✔
739
                                        },
1✔
740
                                        {
1✔
741
                                                Name:       "virtualmachines/expand-spec",
1✔
742
                                                Namespaced: true,
1✔
743
                                        },
1✔
744
                                        {
1✔
745
                                                Name:       "virtualmachines/objectgraph",
1✔
746
                                                Namespaced: true,
1✔
747
                                        },
1✔
748
                                        {
1✔
749
                                                Name:       "virtualmachines/evacuate/cancel",
1✔
750
                                                Namespaced: true,
1✔
751
                                        },
1✔
752
                                        {
1✔
753
                                                Name:       "virtualmachineinstances/guestosinfo",
1✔
754
                                                Namespaced: true,
1✔
755
                                        },
1✔
756
                                        {
1✔
757
                                                Name:       "virtualmachineinstances/userlist",
1✔
758
                                                Namespaced: true,
1✔
759
                                        },
1✔
760
                                        {
1✔
761
                                                Name:       "virtualmachineinstances/filesystemlist",
1✔
762
                                                Namespaced: true,
1✔
763
                                        },
1✔
764
                                        {
1✔
765
                                                Name:       "virtualmachineinstances/addvolume",
1✔
766
                                                Namespaced: true,
1✔
767
                                        },
1✔
768
                                        {
1✔
769
                                                Name:       "virtualmachineinstances/removevolume",
1✔
770
                                                Namespaced: true,
1✔
771
                                        },
1✔
772
                                        {
1✔
773
                                                Name:       "virtualmachineinstances/objectgraph",
1✔
774
                                                Namespaced: true,
1✔
775
                                        },
1✔
776
                                        {
1✔
777
                                                Name:       "virtualmachineinstances/sev/fetchcertchain",
1✔
778
                                                Namespaced: true,
1✔
779
                                        },
1✔
780
                                        {
1✔
781
                                                Name:       "virtualmachineinstances/sev/querylaunchmeasurement",
1✔
782
                                                Namespaced: true,
1✔
783
                                        },
1✔
784
                                        {
1✔
785
                                                Name:       "virtualmachineinstances/sev/setupsession",
1✔
786
                                                Namespaced: true,
1✔
787
                                        },
1✔
788
                                        {
1✔
789
                                                Name:       "virtualmachineinstances/sev/injectlaunchsecret",
1✔
790
                                                Namespaced: true,
1✔
791
                                        },
1✔
792
                                        {
1✔
793
                                                Name:       "virtualmachineinstances/evacuate/cancel",
1✔
794
                                                Namespaced: true,
1✔
795
                                        },
1✔
796
                                }
1✔
797

1✔
798
                                response.WriteAsJson(list)
1✔
799
                        }).
1✔
800
                        Operation(version.Version+"getAPISubResources").
801
                        Doc("Get a KubeVirt API resources").
802
                        Returns(http.StatusOK, "OK", metav1.APIResourceList{}).
803
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, ""))
804

805
                restful.Add(subws)
14✔
806

14✔
807
                subwss = append(subwss, subws)
14✔
808
        }
809
        ws := new(restful.WebService)
7✔
810

7✔
811
        // K8s needs the ability to query the root paths
7✔
812
        ws.Route(ws.GET("/").
7✔
813
                Produces(restful.MIME_JSON).Writes(metav1.RootPaths{}).
7✔
814
                To(func(request *restful.Request, response *restful.Response) {
8✔
815
                        paths := []string{
1✔
816
                                "/apis",
1✔
817
                                "/openapi/v2",
1✔
818
                        }
1✔
819
                        for _, version := range v1.SubresourceGroupVersions {
3✔
820
                                paths = append(paths, definitions.GroupBasePath(version))
2✔
821
                                paths = append(paths, definitions.GroupVersionBasePath(version))
2✔
822
                        }
2✔
823
                        response.WriteAsJson(&metav1.RootPaths{
1✔
824
                                Paths: paths,
1✔
825
                        })
1✔
826
                }).
827
                Operation("getRootPaths").
828
                Doc("Get KubeVirt API root paths").
829
                Returns(http.StatusOK, "OK", metav1.RootPaths{}).
830
                Returns(http.StatusNotFound, httpStatusNotFoundMessage, ""))
831
        ws.Route(ws.GET("/healthz").To(healthz.KubeConnectionHealthzFuncFactory(app.clusterConfig, apiHealthVersion)).Doc("Health endpoint"))
7✔
832

7✔
833
        componentProfiler := profiler.NewProfileManager(app.clusterConfig)
7✔
834

7✔
835
        ws.Route(ws.GET("/start-profiler").To(componentProfiler.HandleStartProfiler).Doc("start profiler endpoint"))
7✔
836
        ws.Route(ws.GET("/stop-profiler").To(componentProfiler.HandleStopProfiler).Doc("stop profiler endpoint"))
7✔
837
        ws.Route(ws.GET("/dump-profiler").To(componentProfiler.HandleDumpProfiler).Doc("dump profiler results endpoint"))
7✔
838

7✔
839
        // K8s needs the ability to query info about a specific API group
7✔
840
        ws.Route(ws.GET(definitions.GroupBasePath(v1.SubresourceGroupVersions[0])).
7✔
841
                Produces(restful.MIME_JSON).Writes(metav1.APIGroup{}).
7✔
842
                To(func(request *restful.Request, response *restful.Response) {
8✔
843
                        response.WriteAsJson(subresourceAPIGroup())
1✔
844
                }).
1✔
845
                Operation(v1.SubresourceGroupVersions[0].Version+"GetSubAPIGroup").
846
                Doc("Get a KubeVirt API Group").
847
                Returns(http.StatusOK, "OK", metav1.APIGroup{}).
848
                Returns(http.StatusNotFound, httpStatusNotFoundMessage, ""))
849

850
        // K8s needs the ability to query the list of API groups this endpoint supports
851
        ws.Route(ws.GET("apis").
7✔
852
                Produces(restful.MIME_JSON).Writes(metav1.APIGroupList{}).
7✔
853
                To(func(request *restful.Request, response *restful.Response) {
8✔
854
                        list := &metav1.APIGroupList{}
1✔
855
                        list.Kind = "APIGroupList"
1✔
856
                        list.Groups = append(list.Groups, subresourceAPIGroup())
1✔
857
                        response.WriteAsJson(list)
1✔
858
                }).
1✔
859
                Operation("getAPIGroupList").
860
                Doc("Get a KubeVirt API GroupList").
861
                Returns(http.StatusOK, "OK", metav1.APIGroupList{}).
862
                Returns(http.StatusNotFound, httpStatusNotFoundMessage, ""))
863

864
        once := sync.Once{}
7✔
865
        var openapispec *spec.Swagger
7✔
866
        ws.Route(ws.GET("openapi/v2").
7✔
867
                Consumes(restful.MIME_JSON).
7✔
868
                Produces(restful.MIME_JSON).
7✔
869
                To(func(request *restful.Request, response *restful.Response) {
7✔
870
                        once.Do(func() {
×
871
                                openapispec = openapi.LoadOpenAPISpec([]*restful.WebService{ws, subwss[0]})
×
872
                                openapispec.Info.Version = virtversion.Get().String()
×
873
                        })
×
874
                        response.WriteAsJson(openapispec)
×
875
                }))
876

877
        restful.Add(ws)
7✔
878
}
879

880
func (app *virtAPIApp) Compose() {
7✔
881

7✔
882
        app.composeSubresources()
7✔
883

7✔
884
        restful.Filter(filter.RequestLoggingFilter())
7✔
885
        restful.Filter(restful.OPTIONSFilter())
7✔
886
        restful.Filter(func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
14✔
887
                allowed, reason, err := app.authorizor.Authorize(req)
7✔
888
                if err != nil {
8✔
889

1✔
890
                        log.Log.Reason(err).Error("internal error during auth request")
1✔
891
                        resp.WriteHeader(http.StatusInternalServerError)
1✔
892
                        return
1✔
893
                } else if allowed {
12✔
894
                        // request is permitted, so proceed with filter chain.
5✔
895
                        chain.ProcessFilter(req, resp)
5✔
896
                        return
5✔
897
                }
5✔
898
                resp.WriteErrorString(http.StatusUnauthorized, reason)
1✔
899
        })
900
}
901

902
func (app *virtAPIApp) ConfigureOpenAPIService() {
×
903
        config := openapi.CreateV3Config()
×
904
        config.GetDefinitions = v12.GetOpenAPIDefinitions
×
905
        spec, err := builderv3.BuildOpenAPISpecFromRoutes(restfuladapter.AdaptWebServices(restful.RegisteredWebServices()), config)
×
906
        if err != nil {
×
907
                panic(err)
×
908
        }
909

910
        ws := new(restful.WebService)
×
911
        ws.Path("/swaggerapi")
×
912
        ws.Produces(restful.MIME_JSON)
×
913
        f := func(req *restful.Request, resp *restful.Response) {
×
914
                resp.WriteAsJson(spec)
×
915
        }
×
916
        ws.Route(ws.GET("/").To(f))
×
917

×
918
        restful.DefaultContainer.Add(ws)
×
919
        http.Handle("/swagger-ui/", http.StripPrefix("/swagger-ui/", http.FileServer(http.Dir(app.SwaggerUI))))
×
920
}
921

922
func deserializeStrings(in string) ([]string, error) {
3✔
923
        if len(in) == 0 {
3✔
924
                return nil, nil
×
925
        }
×
926
        var ret []string
3✔
927
        if err := json.Unmarshal([]byte(in), &ret); err != nil {
3✔
928
                return nil, err
×
929
        }
×
930
        return ret, nil
3✔
931
}
932

933
func (app *virtAPIApp) readRequestHeader() error {
3✔
934
        authConfigMap, err := app.virtCli.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(context.Background(), util.ExtensionAPIServerAuthenticationConfigMap, metav1.GetOptions{})
3✔
935
        if err != nil {
4✔
936
                return err
1✔
937
        }
1✔
938

939
        // The request-header CA is mandatory. It can be retrieved from the configmap as we do here, or it must be provided
940
        // via flag on start of this apiserver. Since we don't do the latter, the former is mandatory for us
941
        // see https://github.com/kubernetes-incubator/apiserver-builder-alpha/blob/master/docs/concepts/auth.md#requestheader-authentication
942
        _, ok := authConfigMap.Data[util.RequestHeaderClientCAFileKey]
2✔
943
        if !ok {
3✔
944
                return fmt.Errorf("requestheader-client-ca-file not found in extension-apiserver-authentication ConfigMap")
1✔
945
        }
1✔
946

947
        // This config map also contains information about what
948
        // headers our authorizor should inspect
949
        headers, ok := authConfigMap.Data["requestheader-username-headers"]
1✔
950
        if ok {
2✔
951
                headerList, err := deserializeStrings(headers)
1✔
952
                if err != nil {
1✔
953
                        return err
×
954
                }
×
955
                app.authorizor.AddUserHeaders(headerList)
1✔
956
        }
957

958
        headers, ok = authConfigMap.Data["requestheader-group-headers"]
1✔
959
        if ok {
2✔
960
                headerList, err := deserializeStrings(headers)
1✔
961
                if err != nil {
1✔
962
                        return err
×
963
                }
×
964
                app.authorizor.AddGroupHeaders(headerList)
1✔
965
        }
966

967
        headers, ok = authConfigMap.Data["requestheader-extra-headers-prefix"]
1✔
968
        if ok {
2✔
969
                headerList, err := deserializeStrings(headers)
1✔
970
                if err != nil {
1✔
971
                        return err
×
972
                }
×
973
                app.authorizor.AddExtraPrefixHeaders(headerList)
1✔
974
        }
975
        return nil
1✔
976
}
977

978
func (app *virtAPIApp) prepareCertManager() {
×
979
        app.certmanager = bootstrap.NewFileCertificateManager(app.tlsCertFilePath, app.tlsKeyFilePath)
×
980
        app.handlerCertManager = bootstrap.NewFileCertificateManager(app.handlerCertFilePath, app.handlerKeyFilePath)
×
981
}
×
982

983
func (app *virtAPIApp) registerValidatingWebhooks(informers *webhooks.Informers) {
×
984
        http.HandleFunc(components.VMICreateValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
985
                validating_webhook.ServeVMICreate(w, r, app.clusterConfig, app.kubeVirtServiceAccounts,
×
986
                        func(field *field.Path, vmiSpec *v1.VirtualMachineInstanceSpec, clusterCfg *virtconfig.ClusterConfig) []metav1.StatusCause {
×
987
                                return netadmitter.Validate(field, vmiSpec, clusterCfg)
×
988
                        },
×
989
                )
990
        })
991
        http.HandleFunc(components.VMIUpdateValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
992
                validating_webhook.ServeVMIUpdate(w, r, app.clusterConfig, app.kubeVirtServiceAccounts)
×
993
        })
×
994
        http.HandleFunc(components.VMValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
995
                validating_webhook.ServeVMs(w, r, app.clusterConfig, app.virtCli, informers, app.kubeVirtServiceAccounts)
×
996
        })
×
997
        http.HandleFunc(components.VMIRSValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
998
                validating_webhook.ServeVMIRS(w, r, app.clusterConfig)
×
999
        })
×
1000
        http.HandleFunc(components.VMPoolValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1001
                validating_webhook.ServeVMPool(w, r, app.clusterConfig, app.kubeVirtServiceAccounts)
×
1002
        })
×
1003
        http.HandleFunc(components.VMIPresetValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1004
                validating_webhook.ServeVMIPreset(w, r)
×
1005
        })
×
1006
        http.HandleFunc(components.MigrationCreateValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1007
                validating_webhook.ServeMigrationCreate(w, r, app.clusterConfig, app.virtCli, app.kubeVirtServiceAccounts)
×
1008
        })
×
1009
        http.HandleFunc(components.MigrationUpdateValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1010
                validating_webhook.ServeMigrationUpdate(w, r)
×
1011
        })
×
1012
        http.HandleFunc(components.VMSnapshotValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1013
                validating_webhook.ServeVMSnapshots(w, r, app.clusterConfig, app.virtCli)
×
1014
        })
×
1015
        http.HandleFunc(components.VMRestoreValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1016
                validating_webhook.ServeVMRestores(w, r, app.clusterConfig, app.virtCli, informers)
×
1017
        })
×
1018
        http.HandleFunc(components.VMBackupValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1019
                validating_webhook.ServeVMBackups(w, r, app.clusterConfig, app.virtCli, informers)
×
1020
        })
×
1021
        http.HandleFunc(components.VMBackupTrackerValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1022
                validating_webhook.ServeVMBackupTrackers(w, r, app.clusterConfig)
×
1023
        })
×
1024
        http.HandleFunc(components.VMExportValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1025
                validating_webhook.ServeVMExports(w, r, app.clusterConfig)
×
1026
        })
×
1027
        http.HandleFunc(components.VMInstancetypeValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1028
                validating_webhook.ServeVmInstancetypes(w, r)
×
1029
        })
×
1030
        http.HandleFunc(components.VMClusterInstancetypeValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1031
                validating_webhook.ServeVmClusterInstancetypes(w, r)
×
1032
        })
×
1033
        http.HandleFunc(components.VMPreferenceValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1034
                validating_webhook.ServeVmPreferences(w, r)
×
1035
        })
×
1036
        http.HandleFunc(components.VMClusterPreferenceValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1037
                validating_webhook.ServeVmClusterPreferences(w, r)
×
1038
        })
×
1039
        http.HandleFunc(components.StatusValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1040
                validating_webhook.ServeStatusValidation(w, r, app.clusterConfig, app.virtCli, informers, app.kubeVirtServiceAccounts)
×
1041
        })
×
1042
        http.HandleFunc(components.PodEvictionValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1043
                validating_webhook.ServePodEvictionInterceptor(w, r, app.clusterConfig, app.virtCli)
×
1044
        })
×
1045
        http.HandleFunc(components.MigrationPolicyCreateValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1046
                validating_webhook.ServeMigrationPolicies(w, r)
×
1047
        })
×
1048
        http.HandleFunc(components.VMCloneCreateValidatePath, func(w http.ResponseWriter, r *http.Request) {
×
1049
                validating_webhook.ServeVirtualMachineClones(w, r, app.clusterConfig, app.virtCli)
×
1050
        })
×
1051
}
1052

1053
func (app *virtAPIApp) registerMutatingWebhook(informers *webhooks.Informers) {
×
1054

×
1055
        http.HandleFunc(components.VMMutatePath, func(w http.ResponseWriter, r *http.Request) {
×
1056
                mutating_webhook.ServeVMs(w, r, app.clusterConfig, app.virtCli)
×
1057
        })
×
1058
        http.HandleFunc(components.VMIMutatePath, func(w http.ResponseWriter, r *http.Request) {
×
1059
                mutating_webhook.ServeVMIs(w, r, app.clusterConfig, informers, app.kubeVirtServiceAccounts)
×
1060
        })
×
1061
        http.HandleFunc(components.MigrationMutatePath, func(w http.ResponseWriter, r *http.Request) {
×
1062
                mutating_webhook.ServeMigrationCreate(w, r)
×
1063
        })
×
1064
        http.HandleFunc(components.VMCloneCreateMutatePath, func(w http.ResponseWriter, r *http.Request) {
×
1065
                mutating_webhook.ServeClones(w, r)
×
1066
        })
×
NEW
1067
        http.HandleFunc(components.VirtLauncherPodMutatePath, func(w http.ResponseWriter, r *http.Request) {
×
NEW
1068
                mutating_webhook.ServeVirtLauncherPods(w, r, app.clusterConfig, app.virtCli)
×
NEW
1069
        })
×
1070
}
1071

1072
func (app *virtAPIApp) setupTLS(k8sCAManager kvtls.KubernetesCAManager, kubevirtCAManager kvtls.ClientCAManager) {
×
1073

×
1074
        // A VerifyClientCertIfGiven request means we're not guaranteed
×
1075
        // a client has been authenticated unless they provide a peer
×
1076
        // cert.
×
1077
        //
×
1078
        // Make sure to verify in subresource endpoint that peer cert
×
1079
        // was provided before processing request. If the peer cert is
×
1080
        // given on the connection, then we can be guaranteed that it
×
1081
        // was signed by the client CA in our pool.
×
1082
        //
×
1083
        // There is another ClientAuth type called 'RequireAndVerifyClientCert'
×
1084
        // We can't use this type here because during the aggregated api status
×
1085
        // check it attempts to hit '/' on our api endpoint to verify an http
×
1086
        // response is given. That status request won't send a peer cert regardless
×
1087
        // if the TLS handshake requests it. As a result, the TLS handshake fails
×
1088
        // and our aggregated endpoint never becomes available.
×
1089
        app.tlsConfig = kvtls.SetupTLSWithCertManager(k8sCAManager, app.certmanager, tls.VerifyClientCertIfGiven, app.clusterConfig)
×
1090
        app.handlerTLSConfiguration = kvtls.SetupTLSForVirtHandlerClients(kubevirtCAManager, app.handlerCertManager, app.externallyManaged)
×
1091
}
×
1092

1093
func (app *virtAPIApp) startTLS(informerFactory controller.KubeInformerFactory) error {
×
1094

×
1095
        errors := make(chan error)
×
1096
        c := make(chan os.Signal, 1)
×
1097

×
1098
        signal.Notify(c, os.Interrupt,
×
1099
                syscall.SIGHUP,
×
1100
                syscall.SIGINT,
×
1101
                syscall.SIGTERM,
×
1102
                syscall.SIGQUIT,
×
1103
        )
×
1104

×
1105
        authConfigMapInformer := informerFactory.ApiAuthConfigMap()
×
1106
        kubevirtCAConfigInformer := informerFactory.KubeVirtCAConfigMap()
×
1107

×
1108
        k8sCAManager := kvtls.NewKubernetesClientCAManager(authConfigMapInformer.GetStore())
×
1109
        kubevirtCAInformer := kvtls.NewCAManager(kubevirtCAConfigInformer.GetStore(), app.namespace, app.caConfigMapName)
×
1110
        app.setupTLS(k8sCAManager, kubevirtCAInformer)
×
1111

×
1112
        app.Compose()
×
1113

×
1114
        http.Handle("/metrics", promhttp.Handler())
×
1115
        server := &http.Server{
×
1116
                Addr:      fmt.Sprintf("%s:%d", app.BindAddress, app.Port),
×
1117
                TLSConfig: app.tlsConfig,
×
1118
                // Disable HTTP/2
×
1119
                // See CVE-2023-44487
×
1120
                TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
×
1121
        }
×
1122

×
1123
        // start TLS server
×
1124
        go func() {
×
1125
                errors <- server.ListenAndServeTLS("", "")
×
1126
        }()
×
1127

1128
        // start graceful shutdown handler
1129
        go func() {
×
1130
                select {
×
1131
                case s := <-c:
×
1132
                        log.Log.Infof("Received signal %s, initiating graceful shutdown", s.String())
×
1133
                case msg := <-app.reInitChan:
×
1134
                        log.Log.Infof("Received signal to reInitialize virt-api [%s], initiating graceful shutdown", msg)
×
1135
                }
1136

1137
                // pause briefly to ensure the load balancer has had a chance to
1138
                // remove this endpoint from rotation due to pod.DeletionTimestamp != nil
1139
                // By pausing, we reduce the chance that the load balancer will attempt to
1140
                // route new requests to the service after we've started the shutdown
1141
                // procedure
1142
                time.Sleep(5 * time.Second)
×
1143

×
1144
                // by default, server.Shutdown() waits indefinitely for all existing
×
1145
                // connections to close. We need to give this a timeout to ensure the
×
1146
                // shutdown will eventually complete.
×
1147
                ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
×
1148
                defer func() {
×
1149
                        cancel()
×
1150
                }()
×
1151

1152
                // Shutdown means new connections are not permitted and it waits for existing
1153
                // connections to terminate (up to the context timeout)
1154
                server.Shutdown(ctx)
×
1155
                // Shutdown forces any existing connections that persist after the shutdown
×
1156
                // times out to be forced closed.
×
1157
                server.Close()
×
1158
        }()
1159

1160
        // wait for server to exit
1161
        err := <-errors
×
1162

×
1163
        if err != nil && err != http.ErrServerClosed {
×
1164
                // ErrServerClosed is an expected error during normal shutdown
×
1165
                return err
×
1166
        }
×
1167
        return nil
×
1168
}
1169

1170
func (app *virtAPIApp) Run() {
×
1171
        host, err := os.Hostname()
×
1172
        if err != nil {
×
1173
                panic(fmt.Errorf("unable to get hostname: %v", err))
×
1174
        }
1175
        app.host = host
×
1176

×
1177
        // get client Cert
×
1178
        err = app.readRequestHeader()
×
1179
        if err != nil {
×
1180
                panic(err)
×
1181
        }
1182

1183
        // Get/Set selfsigned cert
1184
        app.prepareCertManager()
×
1185

×
1186
        // Run informers for webhooks usage
×
1187
        kubeInformerFactory := controller.NewKubeInformerFactory(app.virtCli.RestClient(), app.virtCli, app.aggregatorClient, app.namespace)
×
1188

×
1189
        kubeVirtInformer := kubeInformerFactory.KubeVirt()
×
1190
        // Wire up health check trigger
×
1191
        kubeVirtInformer.SetWatchErrorHandler(func(r *cache.Reflector, err error) {
×
1192
                apiHealthVersion.Clear()
×
1193
                cache.DefaultWatchErrorHandler(context.TODO(), r, err)
×
1194
        })
×
1195

1196
        kubeInformerFactory.ApiAuthConfigMap()
×
1197
        kubeInformerFactory.KubeVirtCAConfigMap()
×
1198
        crdInformer := kubeInformerFactory.CRD()
×
1199
        vmiPresetInformer := kubeInformerFactory.VirtualMachinePreset()
×
1200
        vmRestoreInformer := kubeInformerFactory.VirtualMachineRestore()
×
1201
        vmBackupInformer := kubeInformerFactory.VirtualMachineBackup()
×
1202
        namespaceInformer := kubeInformerFactory.Namespace()
×
1203

×
1204
        stopChan := make(chan struct{}, 1)
×
1205
        defer close(stopChan)
×
1206
        kubeInformerFactory.Start(stopChan)
×
1207
        kubeInformerFactory.WaitForCacheSync(stopChan)
×
1208

×
1209
        app.clusterConfig, err = virtconfig.NewClusterConfig(crdInformer, kubeVirtInformer, app.namespace)
×
1210
        if err != nil {
×
1211
                panic(err)
×
1212
        }
1213
        app.hasCDIDataSource = app.clusterConfig.HasDataSourceAPI()
×
1214
        app.clusterConfig.SetConfigModifiedCallback(app.configModificationCallback)
×
1215
        app.clusterConfig.SetConfigModifiedCallback(app.shouldChangeLogVerbosity)
×
1216
        app.clusterConfig.SetConfigModifiedCallback(app.shouldChangeRateLimiter)
×
1217

×
1218
        var dataSourceInformer cache.SharedIndexInformer
×
1219
        if app.hasCDIDataSource {
×
1220
                dataSourceInformer = kubeInformerFactory.DataSource()
×
1221
                log.Log.Infof("CDI detected, DataSource integration enabled")
×
1222
        } else {
×
1223
                // Add a dummy DataSource informer in the event datasource support
×
1224
                // is disabled. This lets the controller continue to work without
×
1225
                // requiring a separate branching code path.
×
1226
                dataSourceInformer = kubeInformerFactory.DummyDataSource()
×
1227
                log.Log.Infof("CDI not detected, DataSource integration disabled")
×
1228
        }
×
1229

1230
        // It is safe to call kubeInformerFactory.Start multiple times.
1231
        // The function is idempotent and will only start the informers that
1232
        // have not been started yet
1233
        kubeInformerFactory.Start(stopChan)
×
1234
        kubeInformerFactory.WaitForCacheSync(stopChan)
×
1235

×
1236
        webhookInformers := &webhooks.Informers{
×
1237
                VMIPresetInformer:  vmiPresetInformer,
×
1238
                VMRestoreInformer:  vmRestoreInformer,
×
1239
                VMBackupInformer:   vmBackupInformer,
×
1240
                DataSourceInformer: dataSourceInformer,
×
1241
                NamespaceInformer:  namespaceInformer,
×
1242
        }
×
1243

×
1244
        // Build webhook subresources
×
1245
        app.registerMutatingWebhook(webhookInformers)
×
1246
        app.registerValidatingWebhooks(webhookInformers)
×
1247

×
1248
        go app.certmanager.Start()
×
1249
        go app.handlerCertManager.Start()
×
1250

×
1251
        // start TLS server
×
1252
        // tls server will only accept connections when fetching a certificate and internal configuration passed once
×
1253
        err = app.startTLS(kubeInformerFactory)
×
1254
        if err != nil {
×
1255
                panic(err)
×
1256
        }
1257

1258
}
1259

1260
// Detects if a config has been applied that requires
1261
// re-initializing virt-api.
1262
func (app *virtAPIApp) configModificationCallback() {
×
1263
        newHasCDI := app.clusterConfig.HasDataSourceAPI()
×
1264
        if newHasCDI != app.hasCDIDataSource {
×
1265
                if newHasCDI {
×
1266
                        log.Log.Infof("Reinitialize virt-api, cdi DataSource api has been introduced")
×
1267
                } else {
×
1268
                        log.Log.Infof("Reinitialize virt-api, cdi DataSource api has been removed")
×
1269
                }
×
1270
                app.reInitChan <- "reinit due to CDI api change"
×
1271
        }
1272
}
1273

1274
// Update virt-api log verbosity on relevant config changes
1275
func (app *virtAPIApp) shouldChangeLogVerbosity() {
×
1276
        verbosity := app.clusterConfig.GetVirtAPIVerbosity(app.host)
×
1277
        log.Log.SetVerbosityLevel(int(verbosity))
×
1278
        log.Log.V(2).Infof("set log verbosity to %d", verbosity)
×
1279
}
×
1280

1281
// Update virt-handler rate limiter
1282
func (app *virtAPIApp) shouldChangeRateLimiter() {
×
1283
        config := app.clusterConfig.GetConfig()
×
1284
        qps := config.APIConfiguration.RestClient.RateLimiter.TokenBucketRateLimiter.QPS
×
1285
        burst := config.APIConfiguration.RestClient.RateLimiter.TokenBucketRateLimiter.Burst
×
1286
        app.reloadableRateLimiter.Set(flowcontrol.NewTokenBucketRateLimiter(qps, burst))
×
1287
        log.Log.V(2).Infof("setting rate limiter for the API to %v QPS and %v Burst", qps, burst)
×
1288
        qps = config.WebhookConfiguration.RestClient.RateLimiter.TokenBucketRateLimiter.QPS
×
1289
        burst = config.WebhookConfiguration.RestClient.RateLimiter.TokenBucketRateLimiter.Burst
×
1290
        app.reloadableWebhookRateLimiter.Set(flowcontrol.NewTokenBucketRateLimiter(qps, burst))
×
1291
        log.Log.V(2).Infof("setting rate limiter for webhooks to %v QPS and %v Burst", qps, burst)
×
1292
}
×
1293

1294
func (app *virtAPIApp) AddFlags() {
1✔
1295
        app.InitFlags()
1✔
1296

1✔
1297
        app.AddCommonFlags()
1✔
1298

1✔
1299
        flag.StringVar(&app.SwaggerUI, "swagger-ui", "third_party/swagger-ui",
1✔
1300
                "swagger-ui location")
1✔
1301
        flag.BoolVar(&app.SubresourcesOnly, "subresources-only", false,
1✔
1302
                "Only serve subresource endpoints")
1✔
1303
        flag.IntVar(&app.consoleServerPort, "console-server-port", DefaultConsoleServerPort,
1✔
1304
                "The port virt-handler listens on for console requests")
1✔
1305
        flag.StringVar(&app.caConfigMapName, "ca-configmap-name", defaultCAConfigMapName,
1✔
1306
                "The name of configmap containing CA certificates to authenticate requests presenting client certificates with matching CommonName")
1✔
1307
        flag.StringVar(&app.tlsCertFilePath, "tls-cert-file", defaultTlsCertFilePath,
1✔
1308
                "File containing the default x509 Certificate for HTTPS")
1✔
1309
        flag.StringVar(&app.tlsKeyFilePath, "tls-key-file", defaultTlsKeyFilePath,
1✔
1310
                "File containing the default x509 private key matching --tls-cert-file")
1✔
1311
        flag.StringVar(&app.handlerCertFilePath, "handler-cert-file", defaultHandlerCertFilePath,
1✔
1312
                "Client certificate used to prove the identity of the virt-api when it must call virt-handler during a request")
1✔
1313
        flag.StringVar(&app.handlerKeyFilePath, "handler-key-file", defaultHandlerKeyFilePath,
1✔
1314
                "Private key for the client certificate used to prove the identity of the virt-api when it must call virt-handler during a request")
1✔
1315
        flag.BoolVar(&app.externallyManaged, "externally-managed", false,
1✔
1316
                "Allow intermediate certificates to be used in building up the chain of trust when certificates are externally managed")
1✔
1317
}
1✔
1318

1319
// GetGsInfo returns the libguestfs-tools image information based on the KubeVirt installation in the namespace.
1320
func (app *virtAPIApp) GetGsInfo() func(_ *restful.Request, response *restful.Response) {
14✔
1321
        return func(_ *restful.Request, response *restful.Response) {
14✔
1322
                var kvConfig virtoperatorutils.KubeVirtDeploymentConfig
×
1323
                kv := app.clusterConfig.GetConfigFromKubeVirtCR()
×
1324
                if kv == nil {
×
1325
                        error_guestfs(fmt.Errorf("Failed getting KubeVirt config"), response)
×
1326
                }
×
1327
                err := json.Unmarshal([]byte(kv.Status.ObservedDeploymentConfig), &kvConfig)
×
1328
                if err != nil {
×
1329
                        error_guestfs(err, response)
×
1330
                        return
×
1331
                }
×
1332
                response.WriteAsJson(kubecli.GuestfsInfo{
×
1333
                        Registry:    kv.Status.ObservedKubeVirtRegistry,
×
1334
                        Tag:         kv.Status.ObservedKubeVirtVersion,
×
1335
                        ImagePrefix: kvConfig.GetImagePrefix(),
×
1336
                        GsImage:     kvConfig.GsImage,
×
1337
                })
×
1338
                return
×
1339
        }
1340
}
1341

1342
func error_guestfs(err error, response *restful.Response) {
×
1343
        res := map[string]interface{}{}
×
1344
        res["guestfs"] = map[string]interface{}{"status": "failed", "error": fmt.Sprintf("%v", err)}
×
1345
        response.WriteHeaderAndJson(http.StatusInternalServerError, res, restful.MIME_JSON)
×
1346
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc