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

kubevirt / kubevirt / b53a81f4-0618-4002-b726-c9c09d13a927

26 Mar 2026 08:12PM UTC coverage: 71.685% (+0.04%) from 71.643%
b53a81f4-0618-4002-b726-c9c09d13a927

push

prow

web-flow
Merge pull request #17042 from awels/fix_missing_source_state_pvc_after_cclm

Ensure migration state is reset after decentralized live migration

27 of 28 new or added lines in 2 files covered. (96.43%)

125 existing lines in 2 files now uncovered.

77246 of 107757 relevant lines covered (71.69%)

473.15 hits per line

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

65.64
/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
        "k8s.io/kube-openapi/pkg/validation/spec"
35

36
        kvtls "kubevirt.io/kubevirt/pkg/util/tls"
37

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

49
        "kubevirt.io/kubevirt/pkg/util/ratelimiter"
50

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

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

80
const (
81
        // Default port that virt-api listens on.
82
        defaultPort = 443
83

84
        // Default address that virt-api listens on.
85
        defaultHost = "0.0.0.0"
86

87
        DefaultConsoleServerPort = 8186
88

89
        defaultCAConfigMapName     = "kubevirt-ca"
90
        defaultTlsCertFilePath     = "/etc/virt-api/certificates/tls.crt"
91
        defaultTlsKeyFilePath      = "/etc/virt-api/certificates/tls.key"
92
        defaultHandlerCertFilePath = "/etc/virt-handler/clientcertificates/tls.crt"
93
        defaultHandlerKeyFilePath  = "/etc/virt-handler/clientcertificates/tls.key"
94

95
        httpStatusNotFoundMessage     = "Not Found"
96
        httpStatusBadRequestMessage   = "Bad Request"
97
        httpStatusInternalServerError = "Internal Server Error"
98
)
99

100
type VirtApi interface {
101
        Compose()
102
        Run()
103
        AddFlags()
104
        Execute()
105
}
106

107
type virtAPIApp struct {
108
        service.ServiceListen
109
        SubresourcesOnly bool
110
        virtCli          kubecli.KubevirtClient
111
        aggregatorClient *aggregatorclient.Clientset
112
        authorizor       rest.VirtApiAuthorizor
113
        certsDirectory   string
114
        clusterConfig    *virtconfig.ClusterConfig
115

116
        namespace               string
117
        host                    string
118
        tlsConfig               *tls.Config
119
        consoleServerPort       int
120
        certmanager             certificate2.Manager
121
        handlerTLSConfiguration *tls.Config
122
        handlerCertManager      certificate2.Manager
123

124
        caConfigMapName              string
125
        tlsCertFilePath              string
126
        tlsKeyFilePath               string
127
        handlerCertFilePath          string
128
        handlerKeyFilePath           string
129
        externallyManaged            bool
130
        reloadableRateLimiter        *ratelimiter.ReloadableRateLimiter
131
        reloadableWebhookRateLimiter *ratelimiter.ReloadableRateLimiter
132

133
        // indicates if controllers were started with or without CDI/DataSource support
134
        hasCDIDataSource bool
135
        // the channel used to trigger re-initialization.
136
        reInitChan chan string
137

138
        kubeVirtServiceAccounts map[string]struct{}
139
}
140

141
var (
142
        _                service.Service = &virtAPIApp{}
143
        apiHealthVersion                 = new(healthz.KubeApiHealthzVersion)
144
)
145

UNCOV
146
func NewVirtApi() VirtApi {
×
UNCOV
147

×
UNCOV
148
        app := &virtAPIApp{}
×
UNCOV
149
        app.BindAddress = defaultHost
×
UNCOV
150
        app.Port = defaultPort
×
UNCOV
151

×
152
        return app
×
153
}
×
154

155
func (app *virtAPIApp) Execute() {
×
156
        if err := metrics.SetupMetrics(); err != nil {
×
157
                panic(err)
×
158
        }
159

UNCOV
160
        app.reloadableRateLimiter = ratelimiter.NewReloadableRateLimiter(flowcontrol.NewTokenBucketRateLimiter(virtconfig.DefaultVirtAPIQPS, virtconfig.DefaultVirtAPIBurst))
×
161
        app.reloadableWebhookRateLimiter = ratelimiter.NewReloadableRateLimiter(flowcontrol.NewTokenBucketRateLimiter(virtconfig.DefaultVirtWebhookClientQPS, virtconfig.DefaultVirtWebhookClientBurst))
×
162

×
163
        clientmetrics.RegisterRestConfigHooks()
×
UNCOV
164
        clientConfig, err := kubecli.GetKubevirtClientConfig()
×
UNCOV
165
        if err != nil {
×
166
                panic(err)
×
167
        }
168
        clientConfig.RateLimiter = app.reloadableRateLimiter
×
169
        app.virtCli, err = kubecli.GetKubevirtClientFromRESTConfig(clientConfig)
×
170
        if err != nil {
×
171
                panic(err)
×
172
        }
173

174
        authorizor, err := rest.NewAuthorizor(app.reloadableWebhookRateLimiter)
×
175
        if err != nil {
×
176
                panic(err)
×
177
        }
178

UNCOV
179
        app.aggregatorClient = aggregatorclient.NewForConfigOrDie(clientConfig)
×
180

×
181
        app.authorizor = authorizor
×
182

×
UNCOV
183
        app.certsDirectory, err = os.MkdirTemp("", "certsdir")
×
UNCOV
184
        if err != nil {
×
185
                panic(err)
×
186
        }
187
        app.namespace, err = clientutil.GetNamespace()
×
188
        if err != nil {
×
189
                panic(err)
×
190
        }
191

UNCOV
192
        app.kubeVirtServiceAccounts = webhooks.KubeVirtServiceAccounts(app.namespace)
×
193

×
194
        app.reInitChan = make(chan string, 10)
×
195

×
UNCOV
196
        app.Run()
×
197
}
198

199
func subresourceAPIGroup() metav1.APIGroup {
2✔
200
        apiGroup := metav1.APIGroup{
2✔
201
                Name: "subresource.kubevirt.io",
2✔
202
                PreferredVersion: metav1.GroupVersionForDiscovery{
2✔
203
                        GroupVersion: v1.SubresourceGroupVersions[0].Group + "/" + v1.SubresourceGroupVersions[0].Version,
2✔
204
                        Version:      v1.SubresourceGroupVersions[0].Version,
2✔
205
                },
2✔
206
        }
2✔
207

2✔
208
        for _, version := range v1.SubresourceGroupVersions {
6✔
209
                apiGroup.Versions = append(apiGroup.Versions, metav1.GroupVersionForDiscovery{
4✔
210
                        GroupVersion: version.Group + "/" + version.Version,
4✔
211
                        Version:      version.Version,
4✔
212
                })
4✔
213
        }
4✔
214
        apiGroup.ServerAddressByClientCIDRs = append(apiGroup.ServerAddressByClientCIDRs, metav1.ServerAddressByClientCIDR{
2✔
215
                ClientCIDR:    "0.0.0.0/0",
2✔
216
                ServerAddress: "",
2✔
217
        })
2✔
218
        apiGroup.Kind = "APIGroup"
2✔
219
        return apiGroup
2✔
220
}
221

222
func (app *virtAPIApp) composeSubresources() {
10✔
223
        var subwss []*restful.WebService
10✔
224

10✔
225
        for _, version := range v1.SubresourceGroupVersions {
30✔
226
                subresourcesvmGVR := schema.GroupVersionResource{Group: version.Group, Version: version.Version, Resource: "virtualmachines"}
20✔
227
                subresourcesvmiGVR := schema.GroupVersionResource{Group: version.Group, Version: version.Version, Resource: "virtualmachineinstances"}
20✔
228
                expandvmspecGVR := schema.GroupVersionResource{Group: version.Group, Version: version.Version, Resource: "expand-vm-spec"}
20✔
229

20✔
230
                subws := new(restful.WebService)
20✔
231
                subws.Doc(fmt.Sprintf("KubeVirt \"%s\" Subresource API.", version.Version))
20✔
232
                subws.Path(definitions.GroupVersionBasePath(version))
20✔
233

20✔
234
                subresourceApp := rest.NewSubresourceAPIApp(app.virtCli, app.consoleServerPort, app.handlerTLSConfiguration, app.clusterConfig)
20✔
235

20✔
236
                restartRouteBuilder := subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("restart")).
20✔
237
                        To(subresourceApp.RestartVMRequestHandler).
20✔
238
                        Consumes(mime.MIME_ANY).
20✔
239
                        Reads(v1.RestartOptions{}).
20✔
240
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
241
                        Operation(version.Version+"Restart").
20✔
242
                        Doc("Restart a VirtualMachine object.").
20✔
243
                        Returns(http.StatusOK, "OK", "").
20✔
244
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
245
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "")
20✔
246
                restartRouteBuilder.ParameterNamed("body").Required(false)
20✔
247
                subws.Route(restartRouteBuilder)
20✔
248

20✔
249
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("migrate")).
20✔
250
                        To(subresourceApp.MigrateVMRequestHandler).
20✔
251
                        Consumes(mime.MIME_ANY).
20✔
252
                        Reads(v1.MigrateOptions{}).
20✔
253
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
254
                        Operation(version.Version+"Migrate").
20✔
255
                        Doc("Migrate a running VirtualMachine to another node.").
20✔
256
                        Returns(http.StatusOK, "OK", "").
20✔
257
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
258
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
259

20✔
260
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("start")).
20✔
261
                        To(subresourceApp.StartVMRequestHandler).
20✔
262
                        Consumes(mime.MIME_ANY).
20✔
263
                        Reads(v1.StartOptions{}).
20✔
264
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
265
                        Operation(version.Version+"Start").
20✔
266
                        Doc("Start a VirtualMachine object.").
20✔
267
                        Returns(http.StatusOK, "OK", "").
20✔
268
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
269
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
270

20✔
271
                stopRouteBuilder := subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("stop")).
20✔
272
                        To(subresourceApp.StopVMRequestHandler).
20✔
273
                        Consumes(mime.MIME_ANY).
20✔
274
                        Reads(v1.StopOptions{}).
20✔
275
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
276
                        Operation(version.Version+"Stop").
20✔
277
                        Doc("Stop a VirtualMachine object.").
20✔
278
                        Returns(http.StatusOK, "OK", "").
20✔
279
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
280
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "")
20✔
281
                stopRouteBuilder.ParameterNamed("body").Required(false)
20✔
282
                subws.Route(stopRouteBuilder)
20✔
283

20✔
284
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("expand-spec")).
20✔
285
                        To(subresourceApp.ExpandSpecVMRequestHandler).
20✔
286
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
287
                        Operation(version.Version+"vm-ExpandSpec").
20✔
288
                        Produces(restful.MIME_JSON).
20✔
289
                        Doc("Get VirtualMachine object with expanded instancetype and preference.").
20✔
290
                        Returns(http.StatusOK, "OK", "").
20✔
291
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
292
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
20✔
293

20✔
294
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("freeze")).
20✔
295
                        To(subresourceApp.FreezeVMIRequestHandler).
20✔
296
                        Consumes(mime.MIME_ANY).
20✔
297
                        Reads(v1.FreezeUnfreezeTimeout{}).
20✔
298
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
299
                        Operation(version.Version+"Freeze").
20✔
300
                        Doc("Freeze a VirtualMachineInstance object.").
20✔
301
                        Returns(http.StatusOK, "OK", "").
20✔
302
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
20✔
303

20✔
304
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("unfreeze")).
20✔
305
                        To(subresourceApp.UnfreezeVMIRequestHandler).
20✔
306
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
307
                        Operation(version.Version+"Unfreeze").
20✔
308
                        Doc("Unfreeze a VirtualMachineInstance object.").
20✔
309
                        Returns(http.StatusOK, "OK", "").
20✔
310
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
20✔
311

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

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

20✔
328
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("pause")).
20✔
329
                        To(subresourceApp.PauseVMIRequestHandler).
20✔
330
                        Consumes(mime.MIME_ANY).
20✔
331
                        Reads(v1.PauseOptions{}).
20✔
332
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
333
                        Operation(version.Version+"Pause").
20✔
334
                        Doc("Pause a VirtualMachineInstance object.").
20✔
335
                        Returns(http.StatusOK, "OK", "").
20✔
336
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
337
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
338

20✔
339
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("unpause")).
20✔
340
                        To(subresourceApp.UnpauseVMIRequestHandler). // handles VMIs as well
20✔
341
                        Consumes(mime.MIME_ANY).
20✔
342
                        Reads(v1.UnpauseOptions{}).
20✔
343
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
344
                        Operation(version.Version+"Unpause").
20✔
345
                        Doc("Unpause a VirtualMachineInstance object.").
20✔
346
                        Returns(http.StatusOK, "OK", "").
20✔
347
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
348
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
349

20✔
350
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("console")).
20✔
351
                        To(subresourceApp.ConsoleRequestHandler).
20✔
352
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
353
                        Operation(version.Version + "Console").
20✔
354
                        Doc("Open a websocket connection to a serial console on the specified VirtualMachineInstance."))
20✔
355

20✔
356
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("vnc")).
20✔
357
                        To(subresourceApp.VNCRequestHandler).
20✔
358
                        Param(definitions.NamespaceParam(subws)).
20✔
359
                        Param(definitions.NameParam(subws)).
20✔
360
                        Param(definitions.PreserveSessionParam(subws)).
20✔
361
                        Operation(version.Version + "VNC").
20✔
362
                        Doc("Open a websocket connection to connect to VNC on the specified VirtualMachineInstance."))
20✔
363
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("vnc/screenshot")).
20✔
364
                        To(subresourceApp.ScreenshotRequestHandler).
20✔
365
                        Param(definitions.NamespaceParam(subws)).
20✔
366
                        Param(definitions.NameParam(subws)).
20✔
367
                        Param(definitions.MoveCursorParam(subws)).
20✔
368
                        Operation(version.Version + "VNCScreenshot").
20✔
369
                        Doc("Get a PNG VNC screenshot of the specified VirtualMachineInstance."))
20✔
370
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR) + definitions.SubResourcePath("usbredir")).
20✔
371
                        To(subresourceApp.USBRedirRequestHandler).
20✔
372
                        Param(definitions.NamespaceParam(subws)).
20✔
373
                        Param(definitions.NameParam(subws)).
20✔
374
                        Operation(version.Version + "usbredir").
20✔
375
                        Doc("Open a websocket connection to connect to USB device on the specified VirtualMachineInstance."))
20✔
376

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

20✔
397
                // VM endpoint
20✔
398
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmGVR) + definitions.SubResourcePath("portforward") + definitions.PortPath).
20✔
399
                        To(subresourceApp.PortForwardRequestHandler(subresourceApp.FetchVirtualMachineInstanceForVM)).
20✔
400
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
401
                        Param(definitions.PortForwardPortParameter(subws)).
20✔
402
                        Operation(version.Version + "vm-PortForward").
20✔
403
                        Doc("Open a websocket connection forwarding traffic to the running VMI for the specified VirtualMachine and port."))
20✔
404
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmGVR) + definitions.SubResourcePath("portforward") + definitions.PortPath + definitions.ProtocolPath).
20✔
405
                        To(subresourceApp.PortForwardRequestHandler(subresourceApp.FetchVirtualMachineInstanceForVM)).
20✔
406
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
407
                        Param(definitions.PortForwardPortParameter(subws)).
20✔
408
                        Param(definitions.PortForwardProtocolParameter(subws)).
20✔
409
                        Operation(version.Version + "vm-PortForwardWithProtocol").
20✔
410
                        Doc("Open a websocket connection forwarding traffic of the specified protocol (either tcp or udp) to the specified VirtualMachine and port."))
20✔
411

20✔
412
                subws.Route(subws.PUT(definitions.NamespacedResourceBasePath(expandvmspecGVR)).
20✔
413
                        To(subresourceApp.ExpandSpecRequestHandler).
20✔
414
                        Param(definitions.NamespaceParam(subws)).
20✔
415
                        Operation(version.Version+"ExpandSpec").
20✔
416
                        Consumes(restful.MIME_JSON).
20✔
417
                        Produces(restful.MIME_JSON).
20✔
418
                        Doc("Expands instancetype and preference into the passed VirtualMachine object.").
20✔
419
                        Returns(http.StatusOK, "OK", "").
20✔
420
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "").
20✔
421
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
20✔
422

20✔
423
                subws.Route(subws.GET(definitions.SubResourcePath("version")).Produces(restful.MIME_JSON).
20✔
424
                        To(func(request *restful.Request, response *restful.Response) {
21✔
425
                                response.WriteAsJson(virtversion.Get())
1✔
426
                        }).Operation(version.Version + "Version"))
1✔
427

428
                subws.Route(subws.GET(definitions.SubResourcePath("start-cluster-profiler")).Produces(restful.MIME_JSON).
20✔
429
                        To(subresourceApp.StartClusterProfilerHandler).
20✔
430
                        Operation(version.Version + "start-cluster-profiler"))
20✔
431

20✔
432
                subws.Route(subws.GET(definitions.SubResourcePath("stop-cluster-profiler")).Produces(restful.MIME_JSON).
20✔
433
                        To(subresourceApp.StopClusterProfilerHandler).
20✔
434
                        Operation(version.Version + "stop-cluster-profiler"))
20✔
435

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

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

20✔
463
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("userlist")).
20✔
464
                        To(subresourceApp.UserList).
20✔
465
                        Consumes(restful.MIME_JSON).
20✔
466
                        Produces(restful.MIME_JSON).
20✔
467
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
468
                        Operation(version.Version+"Userlist").
20✔
469
                        Doc("Get list of active users via guest agent").
20✔
470
                        Writes(v1.VirtualMachineInstanceGuestOSUserList{}).
20✔
471
                        Returns(http.StatusOK, "OK", v1.VirtualMachineInstanceGuestOSUserList{}))
20✔
472

20✔
473
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("filesystemlist")).
20✔
474
                        To(subresourceApp.FilesystemList).
20✔
475
                        Consumes(restful.MIME_JSON).
20✔
476
                        Produces(restful.MIME_JSON).
20✔
477
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
478
                        Operation(version.Version+"Filesystemlist").
20✔
479
                        Doc("Get list of active filesystems on guest machine via guest agent").
20✔
480
                        Writes(v1.VirtualMachineInstanceFileSystemList{}).
20✔
481
                        Returns(http.StatusOK, "OK", v1.VirtualMachineInstanceFileSystemList{}))
20✔
482

20✔
483
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("objectgraph")).
20✔
484
                        To(subresourceApp.VMIObjectGraph).
20✔
485
                        Consumes(restful.MIME_JSON).
20✔
486
                        Reads(v1.ObjectGraphOptions{}).
20✔
487
                        Produces(restful.MIME_JSON).
20✔
488
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
489
                        Operation(version.Version+"vmi-objectgraph").
20✔
490
                        Doc("Get graph of objects related to a Virtual Machine Instance").
20✔
491
                        Writes(v1.ObjectGraphNode{}).
20✔
492
                        Returns(http.StatusOK, "OK", v1.ObjectGraphNode{}))
20✔
493

20✔
494
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("objectgraph")).
20✔
495
                        To(subresourceApp.VMObjectGraph).
20✔
496
                        Consumes(restful.MIME_JSON).
20✔
497
                        Reads(v1.ObjectGraphOptions{}).
20✔
498
                        Produces(restful.MIME_JSON).
20✔
499
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
500
                        Operation(version.Version+"vm-objectgraph").
20✔
501
                        Doc("Get graph of objects related to a Virtual Machine").
20✔
502
                        Writes(v1.ObjectGraphNode{}).
20✔
503
                        Returns(http.StatusOK, "OK", v1.ObjectGraphNode{}))
20✔
504

20✔
505
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("addvolume")).
20✔
506
                        To(subresourceApp.VMIAddVolumeRequestHandler).
20✔
507
                        Consumes(mime.MIME_ANY).
20✔
508
                        Reads(v1.AddVolumeOptions{}).
20✔
509
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
510
                        Operation(version.Version+"vmi-addvolume").
20✔
511
                        Doc("Add a volume and disk to a running Virtual Machine Instance").
20✔
512
                        Returns(http.StatusOK, "OK", "").
20✔
513
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
514

20✔
515
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("removevolume")).
20✔
516
                        To(subresourceApp.VMIRemoveVolumeRequestHandler).
20✔
517
                        Consumes(mime.MIME_ANY).
20✔
518
                        Reads(v1.RemoveVolumeOptions{}).
20✔
519
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
520
                        Operation(version.Version+"vmi-removevolume").
20✔
521
                        Doc("Removes a volume and disk from a running Virtual Machine Instance").
20✔
522
                        Returns(http.StatusOK, "OK", "").
20✔
523
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
524

20✔
525
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("addvolume")).
20✔
526
                        To(subresourceApp.VMAddVolumeRequestHandler).
20✔
527
                        Consumes(mime.MIME_ANY).
20✔
528
                        Reads(v1.AddVolumeOptions{}).
20✔
529
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
530
                        Operation(version.Version+"vm-addvolume").
20✔
531
                        Doc("Add a volume and disk to a running Virtual Machine.").
20✔
532
                        Returns(http.StatusOK, "OK", "").
20✔
533
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
534

20✔
535
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("removevolume")).
20✔
536
                        To(subresourceApp.VMRemoveVolumeRequestHandler).
20✔
537
                        Consumes(mime.MIME_ANY).
20✔
538
                        Reads(v1.RemoveVolumeOptions{}).
20✔
539
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
540
                        Operation(version.Version+"vm-removevolume").
20✔
541
                        Doc("Removes a volume and disk from a running Virtual Machine.").
20✔
542
                        Returns(http.StatusOK, "OK", "").
20✔
543
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
544

20✔
545
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("memorydump")).
20✔
546
                        To(subresourceApp.MemoryDumpVMRequestHandler).
20✔
547
                        Consumes(mime.MIME_ANY).
20✔
548
                        Reads(v1.VirtualMachineMemoryDumpRequest{}).
20✔
549
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
550
                        Operation(version.Version+"MemoryDump").
20✔
551
                        Doc("Dumps a VirtualMachineInstance memory.").
20✔
552
                        Returns(http.StatusOK, "OK", "").
20✔
553
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
20✔
554

20✔
555
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("removememorydump")).
20✔
556
                        To(subresourceApp.RemoveMemoryDumpVMRequestHandler).
20✔
557
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
558
                        Operation(version.Version+"RemoveMemoryDump").
20✔
559
                        Doc("Remove memory dump association.").
20✔
560
                        Returns(http.StatusOK, "OK", "").
20✔
561
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
20✔
562

20✔
563
                // AMD SEV endpoints
20✔
564
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("sev/fetchcertchain")).
20✔
565
                        To(subresourceApp.SEVFetchCertChainRequestHandler).
20✔
566
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
567
                        Consumes(restful.MIME_JSON).
20✔
568
                        Produces(restful.MIME_JSON).
20✔
569
                        Operation(version.Version+"SEVFetchCertChain").
20✔
570
                        Doc("Fetch SEV certificate chain from the node where Virtual Machine is scheduled").
20✔
571
                        Writes(v1.SEVPlatformInfo{}).
20✔
572
                        Returns(http.StatusOK, "OK", v1.SEVPlatformInfo{}))
20✔
573

20✔
574
                subws.Route(subws.GET(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("sev/querylaunchmeasurement")).
20✔
575
                        To(subresourceApp.SEVQueryLaunchMeasurementHandler).
20✔
576
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
577
                        Consumes(restful.MIME_JSON).
20✔
578
                        Produces(restful.MIME_JSON).
20✔
579
                        Operation(version.Version+"SEVQueryLaunchMeasurement").
20✔
580
                        Doc("Query SEV launch measurement from a Virtual Machine").
20✔
581
                        Writes(v1.SEVMeasurementInfo{}).
20✔
582
                        Returns(http.StatusOK, "OK", v1.SEVMeasurementInfo{}))
20✔
583

20✔
584
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("sev/setupsession")).
20✔
585
                        To(subresourceApp.SEVSetupSessionHandler).
20✔
586
                        Consumes(mime.MIME_ANY).
20✔
587
                        Reads(v1.SEVSessionOptions{}).
20✔
588
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
589
                        Operation(version.Version+"SEVSetupSession").
20✔
590
                        Doc("Setup SEV session parameters for a Virtual Machine").
20✔
591
                        Returns(http.StatusOK, "OK", "").
20✔
592
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
593

20✔
594
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("sev/injectlaunchsecret")).
20✔
595
                        To(subresourceApp.SEVInjectLaunchSecretHandler).
20✔
596
                        Consumes(mime.MIME_ANY).
20✔
597
                        Reads(v1.SEVSecretOptions{}).
20✔
598
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
599
                        Operation(version.Version+"SEVInjectLaunchSecret").
20✔
600
                        Doc("Inject SEV launch secret into a Virtual Machine").
20✔
601
                        Returns(http.StatusOK, "OK", "").
20✔
602
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
603

20✔
604
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmGVR)+definitions.SubResourcePath("evacuate/cancel")).
20✔
605
                        To(subresourceApp.EvacuateCancelHandler(subresourceApp.FetchVirtualMachineInstanceForVM)).
20✔
606
                        Consumes(mime.MIME_ANY).
20✔
607
                        Reads(v1.EvacuateCancelOptions{}).
20✔
608
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
609
                        Operation(version.Version+"vm-evacuatecancel").
20✔
610
                        Doc("Cancel evacuation Virtual Machine").
20✔
611
                        Returns(http.StatusOK, "OK", "").
20✔
612
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
613
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "").
20✔
614
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
20✔
615

20✔
616
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("evacuate/cancel")).
20✔
617
                        To(subresourceApp.EvacuateCancelHandler(subresourceApp.FetchVirtualMachineInstance)).
20✔
618
                        Consumes(mime.MIME_ANY).
20✔
619
                        Reads(v1.EvacuateCancelOptions{}).
20✔
620
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
621
                        Operation(version.Version+"vmi-evacuatecancel").
20✔
622
                        Doc("Cancel evacuation Virtual Machine Instance").
20✔
623
                        Returns(http.StatusOK, "OK", "").
20✔
624
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
625
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, "").
20✔
626
                        Returns(http.StatusInternalServerError, httpStatusInternalServerError, ""))
20✔
627

20✔
628
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("backup")).
20✔
629
                        To(subresourceApp.BackupVMIRequestHandler).
20✔
630
                        Consumes(mime.MIME_ANY).
20✔
631
                        Reads(backupv1.BackupOptions{}).
20✔
632
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
633
                        Operation(version.Version+"Backup").
20✔
634
                        Doc("Initiate a VirtualMachineInstance backup.").
20✔
635
                        Returns(http.StatusOK, "OK", "").
20✔
636
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
637
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
638

20✔
639
                subws.Route(subws.PUT(definitions.NamespacedResourcePath(subresourcesvmiGVR)+definitions.SubResourcePath("redefine-checkpoint")).
20✔
640
                        To(subresourceApp.RedefineCheckpointVMIRequestHandler).
20✔
641
                        Consumes(mime.MIME_ANY).
20✔
642
                        Reads(backupv1.BackupCheckpoint{}).
20✔
643
                        Param(definitions.NamespaceParam(subws)).Param(definitions.NameParam(subws)).
20✔
644
                        Operation(version.Version+"RedefineCheckpoint").
20✔
645
                        Doc("Redefine a checkpoint for a VirtualMachineInstance.").
20✔
646
                        Returns(http.StatusOK, "OK", "").
20✔
647
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, "").
20✔
648
                        Returns(http.StatusBadRequest, httpStatusBadRequestMessage, ""))
20✔
649

20✔
650
                // Return empty api resource list.
20✔
651
                // K8s expects to be able to retrieve a resource list for each aggregated
20✔
652
                // app in order to discover what resources it provides. Without returning
20✔
653
                // an empty list here, there's a bug in the k8s resource discovery that
20✔
654
                // breaks kubectl's ability to reference short names for resources.
20✔
655
                subws.Route(subws.GET("/").
20✔
656
                        Produces(restful.MIME_JSON).Writes(metav1.APIResourceList{}).
20✔
657
                        To(func(request *restful.Request, response *restful.Response) {
21✔
658
                                list := &metav1.APIResourceList{}
1✔
659

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

1✔
790
                                response.WriteAsJson(list)
1✔
791
                        }).
1✔
792
                        Operation(version.Version+"getAPISubResources").
793
                        Doc("Get a KubeVirt API resources").
794
                        Returns(http.StatusOK, "OK", metav1.APIResourceList{}).
795
                        Returns(http.StatusNotFound, httpStatusNotFoundMessage, ""))
796

797
                restful.Add(subws)
20✔
798

20✔
799
                subwss = append(subwss, subws)
20✔
800
        }
801

802
        ws := new(restful.WebService)
10✔
803

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

10✔
826
        componentProfiler := profiler.NewProfileManager(app.clusterConfig)
10✔
827

10✔
828
        ws.Route(ws.GET("/start-profiler").To(componentProfiler.HandleStartProfiler).Doc("start profiler endpoint"))
10✔
829
        ws.Route(ws.GET("/stop-profiler").To(componentProfiler.HandleStopProfiler).Doc("stop profiler endpoint"))
10✔
830
        ws.Route(ws.GET("/dump-profiler").To(componentProfiler.HandleDumpProfiler).Doc("dump profiler results endpoint"))
10✔
831

10✔
832
        // K8s needs the ability to query info about a specific API group
10✔
833
        ws.Route(ws.GET(definitions.GroupBasePath(v1.SubresourceGroupVersions[0])).
10✔
834
                Produces(restful.MIME_JSON).Writes(metav1.APIGroup{}).
10✔
835
                To(func(request *restful.Request, response *restful.Response) {
11✔
836
                        response.WriteAsJson(subresourceAPIGroup())
1✔
837
                }).
1✔
838
                Operation(v1.SubresourceGroupVersions[0].Version+"GetSubAPIGroup").
839
                Doc("Get a KubeVirt API Group").
840
                Returns(http.StatusOK, "OK", metav1.APIGroup{}).
841
                Returns(http.StatusNotFound, httpStatusNotFoundMessage, ""))
842

843
        // K8s needs the ability to query the list of API groups this endpoint supports
844
        ws.Route(ws.GET("apis").
10✔
845
                Produces(restful.MIME_JSON).Writes(metav1.APIGroupList{}).
10✔
846
                To(func(request *restful.Request, response *restful.Response) {
11✔
847
                        list := &metav1.APIGroupList{}
1✔
848
                        list.Kind = "APIGroupList"
1✔
849
                        list.Groups = append(list.Groups, subresourceAPIGroup())
1✔
850
                        response.WriteAsJson(list)
1✔
851
                }).
1✔
852
                Operation("getAPIGroupList").
853
                Doc("Get a KubeVirt API GroupList").
854
                Returns(http.StatusOK, "OK", metav1.APIGroupList{}).
855
                Returns(http.StatusNotFound, httpStatusNotFoundMessage, ""))
856

857
        once := sync.Once{}
10✔
858
        var openapispec *spec.Swagger
10✔
859
        ws.Route(ws.GET("openapi/v2").
10✔
860
                Consumes(restful.MIME_JSON).
10✔
861
                Produces(restful.MIME_JSON).
10✔
862
                To(func(request *restful.Request, response *restful.Response) {
10✔
UNCOV
863
                        once.Do(func() {
×
UNCOV
864
                                openapispec = openapi.LoadOpenAPISpec([]*restful.WebService{ws, subwss[0]})
×
UNCOV
865
                                openapispec.Info.Version = virtversion.Get().String()
×
UNCOV
866
                        })
×
UNCOV
867
                        response.WriteAsJson(openapispec)
×
868
                }))
869

870
        openApiV3Spec, err := openapi.NewV3Spec(subwss)
10✔
871
        if err != nil {
10✔
872
                panic(fmt.Errorf("failed to prepare OpenAPI v3 specs: %w", err))
×
873
        }
874

875
        ws.Route(ws.GET("openapi/v3").
10✔
876
                Produces(restful.MIME_JSON).
10✔
877
                To(func(request *restful.Request, response *restful.Response) {
11✔
878
                        openApiV3Spec.HandleDiscovery(response.ResponseWriter, request.Request)
1✔
879
                }).Operation("getOpenAPIV3Discovery").
1✔
880
                Doc("Get OpenAPI v3 discovery"))
881

882
        for _, version := range v1.SubresourceGroupVersions {
30✔
883
                ws.Route(ws.GET(fmt.Sprintf("openapi/v3/apis/%s/%s", version.Group, version.Version)).
20✔
884
                        Produces(restful.MIME_JSON).
20✔
885
                        To(func(request *restful.Request, response *restful.Response) {
22✔
886
                                openApiV3Spec.HandleGroupVersion(response.ResponseWriter, request.Request)
2✔
887
                        }).
2✔
888
                        Operation(fmt.Sprintf("getOpenAPIV3Spec_%s_%s", version.Group, version.Version)).
889
                        Doc(fmt.Sprintf("Get OpenAPI v3 specification for %s/%s", version.Group, version.Version)).
890
                        Returns(http.StatusOK, "OK", "").
891
                        Returns(http.StatusInternalServerError, "Internal Server Error", ""))
892
        }
893
        restful.Add(ws)
10✔
894
}
895

896
func (app *virtAPIApp) Compose() {
10✔
897

10✔
898
        app.composeSubresources()
10✔
899

10✔
900
        restful.Filter(filter.RequestLoggingFilter())
10✔
901
        restful.Filter(restful.OPTIONSFilter())
10✔
902
        restful.Filter(func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
20✔
903
                allowed, reason, err := app.authorizor.Authorize(req)
10✔
904
                if err != nil {
11✔
905

1✔
906
                        log.Log.Reason(err).Error("internal error during auth request")
1✔
907
                        resp.WriteHeader(http.StatusInternalServerError)
1✔
908
                        return
1✔
909
                } else if allowed {
18✔
910
                        // request is permitted, so proceed with filter chain.
8✔
911
                        chain.ProcessFilter(req, resp)
8✔
912
                        return
8✔
913
                }
8✔
914
                resp.WriteErrorString(http.StatusUnauthorized, reason)
1✔
915
        })
916
}
917

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

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

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

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

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

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

UNCOV
974
func (app *virtAPIApp) prepareCertManager() {
×
UNCOV
975
        app.certmanager = bootstrap.NewFileCertificateManager(app.tlsCertFilePath, app.tlsKeyFilePath)
×
UNCOV
976
        app.handlerCertManager = bootstrap.NewFileCertificateManager(app.handlerCertFilePath, app.handlerKeyFilePath)
×
UNCOV
977
}
×
978

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

1049
func (app *virtAPIApp) registerMutatingWebhook(informers *webhooks.Informers) {
×
1050

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

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

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

1089
func (app *virtAPIApp) startTLS(informerFactory controller.KubeInformerFactory) error {
×
1090

×
1091
        errors := make(chan error)
×
UNCOV
1092
        c := make(chan os.Signal, 1)
×
1093

×
1094
        signal.Notify(c, os.Interrupt,
×
1095
                syscall.SIGHUP,
×
1096
                syscall.SIGINT,
×
1097
                syscall.SIGTERM,
×
1098
                syscall.SIGQUIT,
×
1099
        )
×
1100

×
1101
        authConfigMapInformer := informerFactory.ApiAuthConfigMap()
×
1102
        kubevirtCAConfigInformer := informerFactory.KubeVirtCAConfigMap()
×
1103

×
1104
        k8sCAManager := kvtls.NewKubernetesClientCAManager(authConfigMapInformer.GetStore())
×
1105
        kubevirtCAInformer := kvtls.NewCAManager(kubevirtCAConfigInformer.GetStore(), app.namespace, app.caConfigMapName)
×
1106
        app.setupTLS(k8sCAManager, kubevirtCAInformer)
×
1107

×
1108
        app.Compose()
×
1109

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

×
1119
        // start TLS server
×
1120
        go func() {
×
1121
                errors <- server.ListenAndServeTLS("", "")
×
1122
        }()
×
1123

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

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

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

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

1156
        // wait for server to exit
1157
        err := <-errors
×
UNCOV
1158

×
UNCOV
1159
        if err != nil && err != http.ErrServerClosed {
×
UNCOV
1160
                // ErrServerClosed is an expected error during normal shutdown
×
1161
                return err
×
1162
        }
×
1163
        return nil
×
1164
}
1165

1166
func (app *virtAPIApp) Run() {
×
1167
        host, err := os.Hostname()
×
UNCOV
1168
        if err != nil {
×
UNCOV
1169
                panic(fmt.Errorf("unable to get hostname: %v", err))
×
1170
        }
1171
        app.host = host
×
1172

×
1173
        // get client Cert
×
UNCOV
1174
        err = app.readRequestHeader()
×
1175
        if err != nil {
×
1176
                panic(err)
×
1177
        }
1178

1179
        // Get/Set selfsigned cert
1180
        app.prepareCertManager()
×
UNCOV
1181

×
UNCOV
1182
        // Run informers for webhooks usage
×
UNCOV
1183
        kubeInformerFactory := controller.NewKubeInformerFactory(app.virtCli.RestClient(), app.virtCli, app.aggregatorClient, app.namespace)
×
1184

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

1192
        kubeInformerFactory.ApiAuthConfigMap()
×
1193
        kubeInformerFactory.KubeVirtCAConfigMap()
×
1194
        crdInformer := kubeInformerFactory.CRD()
×
UNCOV
1195
        vmiPresetInformer := kubeInformerFactory.VirtualMachinePreset()
×
1196
        vmRestoreInformer := kubeInformerFactory.VirtualMachineRestore()
×
1197
        vmBackupInformer := kubeInformerFactory.VirtualMachineBackup()
×
1198
        namespaceInformer := kubeInformerFactory.Namespace()
×
1199

×
1200
        stopChan := make(chan struct{}, 1)
×
1201
        defer close(stopChan)
×
1202
        kubeInformerFactory.Start(stopChan)
×
1203
        kubeInformerFactory.WaitForCacheSync(stopChan)
×
1204

×
1205
        app.clusterConfig, err = virtconfig.NewClusterConfig(crdInformer, kubeVirtInformer, app.namespace)
×
1206
        if err != nil {
×
1207
                panic(err)
×
1208
        }
1209
        app.hasCDIDataSource = app.clusterConfig.HasDataSourceAPI()
×
1210
        app.clusterConfig.SetConfigModifiedCallback(app.configModificationCallback)
×
1211
        app.clusterConfig.SetConfigModifiedCallback(app.shouldChangeLogVerbosity)
×
UNCOV
1212
        app.clusterConfig.SetConfigModifiedCallback(app.shouldChangeRateLimiter)
×
1213

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

1226
        // It is safe to call kubeInformerFactory.Start multiple times.
1227
        // The function is idempotent and will only start the informers that
1228
        // have not been started yet
UNCOV
1229
        kubeInformerFactory.Start(stopChan)
×
UNCOV
1230
        kubeInformerFactory.WaitForCacheSync(stopChan)
×
UNCOV
1231

×
UNCOV
1232
        webhookInformers := &webhooks.Informers{
×
1233
                VMIPresetInformer:  vmiPresetInformer,
×
1234
                VMRestoreInformer:  vmRestoreInformer,
×
1235
                VMBackupInformer:   vmBackupInformer,
×
1236
                DataSourceInformer: dataSourceInformer,
×
1237
                NamespaceInformer:  namespaceInformer,
×
1238
        }
×
1239

×
1240
        // Build webhook subresources
×
1241
        app.registerMutatingWebhook(webhookInformers)
×
1242
        app.registerValidatingWebhooks(webhookInformers)
×
1243

×
1244
        go app.certmanager.Start()
×
1245
        go app.handlerCertManager.Start()
×
1246

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

1254
}
1255

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

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

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

1290
func (app *virtAPIApp) AddFlags() {
×
1291
        app.InitFlags()
×
1292

×
UNCOV
1293
        app.AddCommonFlags()
×
UNCOV
1294

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

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

1336
func error_guestfs(err error, response *restful.Response) {
×
1337
        res := map[string]interface{}{}
×
1338
        res["guestfs"] = map[string]interface{}{"status": "failed", "error": fmt.Sprintf("%v", err)}
×
UNCOV
1339
        response.WriteHeaderAndJson(http.StatusInternalServerError, res, restful.MIME_JSON)
×
UNCOV
1340
}
×
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