Skip to content

Commit c769c18

Browse files
committed
feat(vm): add uptime printable column
Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
1 parent 863e22f commit c769c18

6 files changed

Lines changed: 106 additions & 0 deletions

File tree

api/core/v1alpha2/virtual_machine.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const (
4242
// +kubebuilder:resource:categories={all,virtualization},scope=Namespaced,shortName={vm},singular=virtualmachine
4343
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="The phase of the virtual machine."
4444
// +kubebuilder:printcolumn:name="PhaseAge",type="date",JSONPath=".status.stats.phasesTransitions[-1].timestamp",description="Time since the virtual machine entered the current phase."
45+
// +kubebuilder:printcolumn:name="Uptime",type="date",JSONPath=".status.runningSince",description="Time since the virtual machine started running."
4546
// +kubebuilder:printcolumn:name="Cores",priority=1,type="string",JSONPath=".spec.cpu.cores",description="The number of cores of the virtual machine."
4647
// +kubebuilder:printcolumn:name="CoreFraction",priority=1,type="string",JSONPath=".spec.cpu.coreFraction",description="Virtual machine core fraction. The range of available values is set in the `sizePolicy` parameter of the VirtualMachineClass; if it is not set, use values within the 1–100% range."
4748
// +kubebuilder:printcolumn:name="Memory",priority=1,type="string",JSONPath=".spec.memory.size",description="The amount of memory of the virtual machine."
@@ -299,6 +300,8 @@ type VirtualMachineStatus struct {
299300
Stats *VirtualMachineStats `json:"stats,omitempty"`
300301
// Migration info.
301302
MigrationState *VirtualMachineMigrationState `json:"migrationState,omitempty"`
303+
// RunningSince is the timestamp when the virtual machine entered the running state.
304+
RunningSince *metav1.Time `json:"runningSince,omitempty"`
302305
// Generating a resource that was last processed by the controller.
303306
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
304307

api/core/v1alpha2/zz_generated.deepcopy.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crds/doc-ru-virtualmachines.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,9 @@ spec:
684684
result:
685685
description: |
686686
Результат миграции: `Succeeded` или `Failed`.
687+
runningSince:
688+
description: |
689+
Время перехода виртуальной машины в состояние running.
687690
stats:
688691
description: Статистика по виртуальной машине.
689692
properties:

crds/virtualmachines.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,11 @@ spec:
11531153
- ""
11541154
- "Succeeded"
11551155
- "Failed"
1156+
runningSince:
1157+
description: Time when the virtual machine entered the running state.
1158+
format: date-time
1159+
nullable: true
1160+
type: string
11561161
stats:
11571162
type: object
11581163
description: Virtual machine statistics.
@@ -1426,6 +1431,10 @@ spec:
14261431
jsonPath: .status.stats.phasesTransitions[-1].timestamp
14271432
name: PhaseAge
14281433
type: date
1434+
- description: Time since the virtual machine started running.
1435+
jsonPath: .status.runningSince
1436+
name: Uptime
1437+
type: date
14291438
- description: Real number of the virtual machine cores.
14301439
jsonPath: .status.resources.cpu.cores
14311440
name: Cores

images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ func (h *LifeCycleHandler) Name() string {
122122

123123
func (h *LifeCycleHandler) syncRunning(ctx context.Context, vm *v1alpha2.VirtualMachine, kvvm *virtv1.VirtualMachine, kvvmi *virtv1.VirtualMachineInstance, pod *corev1.Pod, log *slog.Logger) error {
124124
cb := conditions.NewConditionBuilder(vmcondition.TypeRunning).Generation(vm.GetGeneration())
125+
defer syncRunningSince(vm)
125126

126127
if pod != nil && pod.Status.Message != "" {
127128
cb.Status(metav1.ConditionFalse).
@@ -229,6 +230,16 @@ func (h *LifeCycleHandler) syncRunning(ctx context.Context, vm *v1alpha2.Virtual
229230
return nil
230231
}
231232

233+
func syncRunningSince(vm *v1alpha2.VirtualMachine) {
234+
running, found := conditions.GetCondition(vmcondition.TypeRunning, vm.Status.Conditions)
235+
if !found || running.Status != metav1.ConditionTrue {
236+
vm.Status.RunningSince = nil
237+
return
238+
}
239+
240+
vm.Status.RunningSince = running.LastTransitionTime.DeepCopy()
241+
}
242+
232243
func (h *LifeCycleHandler) checkVMPodVolumeErrors(ctx context.Context, vm *v1alpha2.VirtualMachine, log *slog.Logger) error {
233244
var podList corev1.PodList
234245
err := h.client.List(ctx, &podList, &client.ListOptions{
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
Copyright 2026 Flant JSC
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+
17+
package internal
18+
19+
import (
20+
"time"
21+
22+
. "github.com/onsi/ginkgo/v2"
23+
. "github.com/onsi/gomega"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
26+
"github.com/deckhouse/virtualization/api/core/v1alpha2"
27+
"github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition"
28+
)
29+
30+
var _ = Describe("LifeCycleHandler", func() {
31+
Describe("syncRunningSince", func() {
32+
It("sets runningSince from the Running condition last transition time", func() {
33+
transitionTime := metav1.NewTime(time.Date(2026, 4, 24, 12, 0, 0, 0, time.UTC))
34+
vm := &v1alpha2.VirtualMachine{
35+
Status: v1alpha2.VirtualMachineStatus{
36+
Conditions: []metav1.Condition{
37+
{
38+
Type: vmcondition.TypeRunning.String(),
39+
Status: metav1.ConditionTrue,
40+
LastTransitionTime: transitionTime,
41+
},
42+
},
43+
},
44+
}
45+
46+
syncRunningSince(vm)
47+
48+
Expect(vm.Status.RunningSince).NotTo(BeNil())
49+
Expect(vm.Status.RunningSince.Time).To(Equal(transitionTime.Time))
50+
})
51+
52+
DescribeTable("clears runningSince when the VM is not running",
53+
func(conditions []metav1.Condition) {
54+
transitionTime := metav1.NewTime(time.Date(2026, 4, 24, 12, 0, 0, 0, time.UTC))
55+
vm := &v1alpha2.VirtualMachine{
56+
Status: v1alpha2.VirtualMachineStatus{
57+
RunningSince: &transitionTime,
58+
Conditions: conditions,
59+
},
60+
}
61+
62+
syncRunningSince(vm)
63+
64+
Expect(vm.Status.RunningSince).To(BeNil())
65+
},
66+
Entry("without the Running condition", nil),
67+
Entry("with the Running condition set to False", []metav1.Condition{
68+
{
69+
Type: vmcondition.TypeRunning.String(),
70+
Status: metav1.ConditionFalse,
71+
LastTransitionTime: metav1.NewTime(time.Date(2026, 4, 24, 12, 0, 0, 0, time.UTC)),
72+
},
73+
}),
74+
)
75+
})
76+
})

0 commit comments

Comments
 (0)