Skip to content

Commit 7e6e26e

Browse files
danegstaCopilot
andcommitted
Add V2 physical container volumes
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 3fa635c commit 7e6e26e

24 files changed

Lines changed: 2580 additions & 19 deletions

api/v2/groupversion_info.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var (
3535
&PhysicalContainerImage{},
3636
&PhysicalContainer{},
3737
&PhysicalContainerNetwork{},
38+
&PhysicalContainerVolume{},
3839
}
3940

4041
// Types that must be recognizable by the API server, but are not persisted.
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
package v2
7+
8+
import (
9+
"context"
10+
"reflect"
11+
"strings"
12+
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/runtime"
15+
"k8s.io/apimachinery/pkg/runtime/schema"
16+
"k8s.io/apimachinery/pkg/types"
17+
"k8s.io/apimachinery/pkg/util/validation/field"
18+
19+
apiserver_resource "github.com/tilt-dev/tilt-apiserver/pkg/server/builder/resource"
20+
apiserver_resourcerest "github.com/tilt-dev/tilt-apiserver/pkg/server/builder/resource/resourcerest"
21+
apiserver_resourcestrategy "github.com/tilt-dev/tilt-apiserver/pkg/server/builder/resource/resourcestrategy"
22+
23+
"github.com/microsoft/dcp/pkg/commonapi"
24+
)
25+
26+
// PhysicalContainerVolumePhase describes the lifecycle phase of a PhysicalContainerVolume.
27+
type PhysicalContainerVolumePhase string
28+
29+
const (
30+
// PhysicalContainerVolumePhasePending indicates that the volume is waiting for prerequisites.
31+
PhysicalContainerVolumePhasePending PhysicalContainerVolumePhase = "Pending"
32+
33+
// PhysicalContainerVolumePhaseReady indicates that the runtime volume is available.
34+
PhysicalContainerVolumePhaseReady PhysicalContainerVolumePhase = "Ready"
35+
36+
// PhysicalContainerVolumePhaseMissing indicates that the referenced runtime volume was not found.
37+
PhysicalContainerVolumePhaseMissing PhysicalContainerVolumePhase = "Missing"
38+
39+
// PhysicalContainerVolumePhaseFailed indicates that creating or inspecting the runtime volume failed.
40+
PhysicalContainerVolumePhaseFailed PhysicalContainerVolumePhase = "Failed"
41+
)
42+
43+
const (
44+
// PhysicalContainerVolumeReasonPending indicates that the volume is waiting for prerequisites.
45+
PhysicalContainerVolumeReasonPending string = "Pending"
46+
47+
// PhysicalContainerVolumeReasonCreating indicates that runtime volume creation is in progress.
48+
PhysicalContainerVolumeReasonCreating string = "Creating"
49+
50+
// PhysicalContainerVolumeReasonCreated indicates that runtime volume creation completed.
51+
PhysicalContainerVolumeReasonCreated string = "Created"
52+
53+
// PhysicalContainerVolumeReasonCreateFailed indicates that runtime volume creation failed.
54+
PhysicalContainerVolumeReasonCreateFailed string = "CreateFailed"
55+
56+
// PhysicalContainerVolumeReasonVolumeReady indicates that the runtime volume is available.
57+
PhysicalContainerVolumeReasonVolumeReady string = "VolumeReady"
58+
59+
// PhysicalContainerVolumeReasonRuntimeVolumeMissing indicates that the runtime volume was not found.
60+
PhysicalContainerVolumeReasonRuntimeVolumeMissing string = "RuntimeVolumeMissing"
61+
62+
// PhysicalContainerVolumeReasonReconciliationFailed indicates that reconciliation failed outside a specific progress gate.
63+
PhysicalContainerVolumeReasonReconciliationFailed string = "ReconciliationFailed"
64+
)
65+
66+
// PhysicalContainerVolumeSpec describes either an existing runtime volume or how to create one.
67+
// +k8s:openapi-gen=true
68+
type PhysicalContainerVolumeSpec struct {
69+
// VolumeID identifies an existing runtime volume to track. Container runtimes commonly use
70+
// the volume name as its identifier. When set, creation fields are forbidden.
71+
VolumeID string `json:"volumeID,omitempty"`
72+
73+
// VolumeName is the runtime name to use when creating a new volume. Required when volumeID is omitted.
74+
VolumeName string `json:"volumeName,omitempty"`
75+
76+
// PreserveOnDeletion keeps the runtime volume in place when this resource is deleted.
77+
// By default the runtime volume is removed, including when this resource only tracks a
78+
// volume it did not create.
79+
PreserveOnDeletion bool `json:"preserveOnDeletion,omitempty"`
80+
81+
// Labels contains labels to apply to a newly-created runtime volume.
82+
// +listType=map
83+
// +listMapKey=key
84+
Labels []commonapi.Label `json:"labels,omitempty"`
85+
}
86+
87+
// PhysicalContainerVolumeStatus describes the observed runtime volume.
88+
// +k8s:openapi-gen=true
89+
type PhysicalContainerVolumeStatus struct {
90+
// Phase summarizes whether the runtime volume is available.
91+
// +kubebuilder:validation:Enum=Pending;Ready;Missing;Failed
92+
// +optional
93+
Phase PhysicalContainerVolumePhase `json:"phase,omitempty"`
94+
95+
// VolumeID is the runtime identifier being tracked.
96+
VolumeID string `json:"volumeID,omitempty"`
97+
98+
// VolumeName is the runtime volume name.
99+
VolumeName string `json:"volumeName,omitempty"`
100+
101+
// Driver is the runtime volume driver.
102+
Driver string `json:"driver,omitempty"`
103+
104+
// MountPoint is the host path where the runtime stores volume data, when exposed by the runtime.
105+
MountPoint string `json:"mountPoint,omitempty"`
106+
107+
// Scope is the runtime volume scope.
108+
Scope string `json:"scope,omitempty"`
109+
110+
// CreatedAt is the runtime volume creation timestamp.
111+
CreatedAt metav1.MicroTime `json:"createdAt,omitempty"`
112+
113+
// Conditions describe readiness and reconciliation progress.
114+
// +listType=map
115+
// +listMapKey=type
116+
// +optional
117+
Conditions []metav1.Condition `json:"conditions,omitempty"`
118+
}
119+
120+
func (pcvs PhysicalContainerVolumeStatus) CopyTo(dest apiserver_resource.ObjectWithStatusSubResource) {
121+
pcvs.DeepCopyInto(&dest.(*PhysicalContainerVolume).Status)
122+
}
123+
124+
// PhysicalContainerVolume represents one runtime container volume in a DCP V2 namespace.
125+
// +kubebuilder:object:root=true
126+
// +kubebuilder:subresource:status
127+
// +k8s:openapi-gen=true
128+
// +kubebuilder:resource:scope=Namespaced,path=physicalcontainervolumes,shortName=pcv
129+
type PhysicalContainerVolume struct {
130+
metav1.TypeMeta `json:",inline"`
131+
metav1.ObjectMeta `json:"metadata,omitempty"`
132+
133+
Spec PhysicalContainerVolumeSpec `json:"spec,omitempty"`
134+
Status PhysicalContainerVolumeStatus `json:"status,omitempty"`
135+
}
136+
137+
func (pv *PhysicalContainerVolume) GetGroupVersionResource() schema.GroupVersionResource {
138+
return schema.GroupVersionResource{
139+
Group: GroupVersion.Group,
140+
Version: GroupVersion.Version,
141+
Resource: "physicalcontainervolumes",
142+
}
143+
}
144+
145+
func (pv *PhysicalContainerVolume) GetObjectMeta() *metav1.ObjectMeta {
146+
return &pv.ObjectMeta
147+
}
148+
149+
func (pv *PhysicalContainerVolume) GetStatus() apiserver_resource.StatusSubResource {
150+
return pv.Status
151+
}
152+
153+
func (pv *PhysicalContainerVolume) New() runtime.Object {
154+
return &PhysicalContainerVolume{}
155+
}
156+
157+
func (pv *PhysicalContainerVolume) NewList() runtime.Object {
158+
return &PhysicalContainerVolumeList{}
159+
}
160+
161+
func (pv *PhysicalContainerVolume) IsStorageVersion() bool {
162+
return true
163+
}
164+
165+
func (pv *PhysicalContainerVolume) NamespaceScoped() bool {
166+
return true
167+
}
168+
169+
func (pv *PhysicalContainerVolume) ShortNames() []string {
170+
return []string{"pcv"}
171+
}
172+
173+
func (pv *PhysicalContainerVolume) NamespacedName() types.NamespacedName {
174+
return NamespacedName(pv)
175+
}
176+
177+
func (pv *PhysicalContainerVolume) Validate(ctx context.Context) field.ErrorList {
178+
errorList := ValidateNamespacedResourceMetadata(pv)
179+
specPath := field.NewPath("spec")
180+
181+
if commonapi.ResourceCreationProhibited.Load() && pv.DeletionTimestamp.IsZero() {
182+
errorList = append(errorList, field.Forbidden(nil, commonapi.ErrResourceCreationProhibited.Error()))
183+
}
184+
185+
errorList = append(errorList, commonapi.ValidateAnnotationsSize(pv.Annotations, field.NewPath("metadata", "annotations"))...)
186+
187+
if pv.Spec.VolumeID != "" {
188+
if strings.TrimSpace(pv.Spec.VolumeID) != pv.Spec.VolumeID {
189+
errorList = append(errorList, field.Invalid(specPath.Child("volumeID"), pv.Spec.VolumeID, "volumeID must not have leading or trailing whitespace"))
190+
}
191+
if pv.Spec.VolumeName != "" {
192+
errorList = append(errorList, field.Forbidden(specPath.Child("volumeName"), "volumeName cannot be set when volumeID is set"))
193+
}
194+
if len(pv.Spec.Labels) > 0 {
195+
errorList = append(errorList, field.Forbidden(specPath.Child("labels"), "labels cannot be set when volumeID is set"))
196+
}
197+
return errorList
198+
}
199+
200+
if strings.TrimSpace(pv.Spec.VolumeName) == "" {
201+
errorList = append(errorList, field.Required(specPath.Child("volumeName"), "volumeName must be set when volumeID is omitted"))
202+
} else if strings.TrimSpace(pv.Spec.VolumeName) != pv.Spec.VolumeName {
203+
errorList = append(errorList, field.Invalid(specPath.Child("volumeName"), pv.Spec.VolumeName, "volumeName must not have leading or trailing whitespace"))
204+
}
205+
206+
errorList = append(errorList, validateLabels(pv.Spec.Labels, specPath.Child("labels"))...)
207+
return errorList
208+
}
209+
210+
// ValidateUpdate freezes the spec of an existing PhysicalContainerVolume.
211+
func (pv *PhysicalContainerVolume) ValidateUpdate(ctx context.Context, old runtime.Object) field.ErrorList {
212+
errorList := field.ErrorList{}
213+
214+
oldPhysicalContainerVolume := old.(*PhysicalContainerVolume)
215+
if !reflect.DeepEqual(oldPhysicalContainerVolume.Spec, pv.Spec) {
216+
errorList = append(errorList, field.Forbidden(field.NewPath("spec"), "spec is immutable"))
217+
}
218+
219+
return errorList
220+
}
221+
222+
// PhysicalContainerVolumeList contains a list of PhysicalContainerVolume instances.
223+
// +k8s:openapi-gen=true
224+
// +kubebuilder:object:root=true
225+
type PhysicalContainerVolumeList struct {
226+
metav1.TypeMeta `json:",inline"`
227+
metav1.ListMeta `json:"metadata,omitempty"`
228+
Items []PhysicalContainerVolume `json:"items"`
229+
}
230+
231+
func (pvl *PhysicalContainerVolumeList) GetListMeta() *metav1.ListMeta {
232+
return &pvl.ListMeta
233+
}
234+
235+
func (pvl *PhysicalContainerVolumeList) ItemCount() uint32 {
236+
return uint32(len(pvl.Items))
237+
}
238+
239+
func (pvl *PhysicalContainerVolumeList) GetItems() []*PhysicalContainerVolume {
240+
retval := make([]*PhysicalContainerVolume, len(pvl.Items))
241+
for i := range pvl.Items {
242+
retval[i] = &pvl.Items[i]
243+
}
244+
return retval
245+
}
246+
247+
func init() {
248+
SchemeBuilder.Register(&PhysicalContainerVolume{}, &PhysicalContainerVolumeList{})
249+
}
250+
251+
// Ensure types support interfaces expected by our API server.
252+
var _ apiserver_resource.Object = (*PhysicalContainerVolume)(nil)
253+
var _ apiserver_resource.ObjectWithStatusSubResource = (*PhysicalContainerVolume)(nil)
254+
var _ apiserver_resource.StatusSubResource = (*PhysicalContainerVolumeStatus)(nil)
255+
var _ apiserver_resource.ObjectList = (*PhysicalContainerVolumeList)(nil)
256+
var _ commonapi.ListWithObjectItems[PhysicalContainerVolume, *PhysicalContainerVolume] = (*PhysicalContainerVolumeList)(nil)
257+
var _ apiserver_resourcerest.ShortNamesProvider = (*PhysicalContainerVolume)(nil)
258+
var _ apiserver_resourcestrategy.Validater = (*PhysicalContainerVolume)(nil)
259+
var _ apiserver_resourcestrategy.ValidateUpdater = (*PhysicalContainerVolume)(nil)

0 commit comments

Comments
 (0)