Skip to content
This repository was archived by the owner on Mar 16, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/apis/api.acorn.io/v1/region.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const (
RegionConditionClusterReady = "ClusterReady"

LocalRegion = "local"
AllRegions = "*"
)

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down
9 changes: 6 additions & 3 deletions pkg/apis/internal.acorn.io/v1/projectinstance.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ type ProjectInstanceSpec struct {
}

type ProjectInstanceStatus struct {
Namespace string `json:"namespace,omitempty"`
DefaultRegion string `json:"defaultRegion,omitempty"`
Namespace string `json:"namespace,omitempty"`
DefaultRegion string `json:"defaultRegion,omitempty"`
// SupportedRegions on the status field should be an explicit list of supported regions.
// That is, if the user specifies "*" for supported regions, then the status value should be the list of all regions.
// This is to avoid having to make another call to explicitly list all regions.
SupportedRegions []string `json:"supportedRegions,omitempty"`
}

Expand All @@ -44,7 +47,7 @@ func (in *ProjectInstance) GetSupportedRegions() []string {
func (in *ProjectInstance) SetDefaultRegion(region string) {
if in.Spec.DefaultRegion == "" && len(in.Spec.SupportedRegions) == 0 {
in.Status.DefaultRegion = region
in.Status.SupportedRegions = []string{region}
in.Status.SupportedRegions = []string{"*"}
} else {
// Set the status values to the provided spec values.
// The idea here is that internally, we only need to check the status values.
Expand Down
4 changes: 2 additions & 2 deletions pkg/client/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (c *DefaultClient) ProjectCreate(ctx context.Context, name, defaultRegion s
}
if defaultRegion != "" {
project.Spec.DefaultRegion = defaultRegion
if !slices.Contains(supportedRegions, defaultRegion) {
if !slices.Contains(supportedRegions, defaultRegion) && !slices.Contains(supportedRegions, apiv1.AllRegions) {
supportedRegions = append([]string{defaultRegion}, supportedRegions...)
}
}
Expand All @@ -57,7 +57,7 @@ func (c *DefaultClient) ProjectUpdate(ctx context.Context, project *apiv1.Projec
project.Spec.SupportedRegions = supportedRegions
}
}
if project.Spec.DefaultRegion != "" && !slices.Contains(project.Spec.SupportedRegions, project.Spec.DefaultRegion) {
if project.Spec.DefaultRegion != "" && !slices.Contains(project.Spec.SupportedRegions, project.Spec.DefaultRegion) && !slices.Contains(project.Spec.SupportedRegions, apiv1.AllRegions) {
project.Spec.SupportedRegions = append(project.Spec.SupportedRegions, project.Spec.DefaultRegion)
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/openapi/generated/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion pkg/project/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
klabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/utils/strings/slices"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
)

func SetProjectSupportedRegions(req router.Request, resp router.Response) error {
req.Object.(*v1.ProjectInstance).SetDefaultRegion(apiv1.LocalRegion)
project := req.Object.(*v1.ProjectInstance)
project.SetDefaultRegion(apiv1.LocalRegion)
if slices.Contains(project.Status.SupportedRegions, apiv1.AllRegions) {
// If the project supports all regions, then ensure the default region and the local region are supported regions.
project.Status.SupportedRegions = []string{project.Status.DefaultRegion}
if project.Status.DefaultRegion != apiv1.LocalRegion {
project.Status.SupportedRegions = append(project.Status.SupportedRegions, apiv1.LocalRegion)
}
}

resp.Objects(req.Object)
return nil
Expand Down
1 change: 1 addition & 0 deletions pkg/project/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func TestSetProjectSupportedRegions(t *testing.T) {
tester.DefaultTest(t, scheme.Scheme, "testdata/setsupportedregions/no-default", SetProjectSupportedRegions)
tester.DefaultTest(t, scheme.Scheme, "testdata/setsupportedregions/with-supported-regions", SetProjectSupportedRegions)
tester.DefaultTest(t, scheme.Scheme, "testdata/setsupportedregions/with-default-and-supported", SetProjectSupportedRegions)
tester.DefaultTest(t, scheme.Scheme, "testdata/setsupportedregions/all-supported-regions-with-default", SetProjectSupportedRegions)
}

func TestCreateNamespace(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
`apiVersion: internal.acorn.io/v1
kind: ProjectInstance
metadata:
creationTimestamp: null
name: acorn
spec:
defaultRegion: other-region
supportedRegions:
- '*'
status:
defaultRegion: other-region
supportedRegions:
- other-region
- local
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
kind: ProjectInstance
apiVersion: internal.acorn.io/v1
metadata:
name: acorn
spec:
defaultRegion: other-region
supportedRegions:
- "*"
32 changes: 14 additions & 18 deletions pkg/server/registry/apigroups/acorn/projects/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,46 +26,42 @@ func (v *Validator) Validate(_ context.Context, obj runtime.Object) field.ErrorL
var result field.ErrorList
project := obj.(*apiv1.Project)

if project.Spec.DefaultRegion != "" && !slices.Contains(project.Spec.SupportedRegions, project.Spec.DefaultRegion) {
if project.Spec.DefaultRegion != "" && !slices.Contains(project.Spec.SupportedRegions, project.Spec.DefaultRegion) && !slices.Contains(project.Spec.SupportedRegions, apiv1.AllRegions) {
return append(result, field.Invalid(field.NewPath("spec", "defaultRegion"), project.Spec.DefaultRegion, "default region is not in the supported regions list"))
}

return nil
}

func (v *Validator) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
func (v *Validator) ValidateUpdate(ctx context.Context, newObj, _ runtime.Object) field.ErrorList {
// Ensure that default region and supported regions are valid.
if err := v.Validate(ctx, obj); err != nil {
if err := v.Validate(ctx, newObj); err != nil {
return err
}

// If the user is removing a supported region, ensure that there are no apps in that region.
oldProject, newProject := old.(*apiv1.Project), obj.(*apiv1.Project)
var removedRegions []string
for _, region := range oldProject.Status.SupportedRegions {
if !slices.Contains(newProject.Status.SupportedRegions, region) {
removedRegions = append(removedRegions, region)
}
}

if len(removedRegions) > 0 {
return v.ensureNoObjectsExistInRegions(ctx, newProject.Name, newProject.Status.SupportedRegions, removedRegions, &apiv1.AppList{}, &apiv1.VolumeList{})
newProject := newObj.(*apiv1.Project)
// If there are no supported regions given by the user (and the above validate call passed) or the user explicitly
// allowed all regions, then the project supports all regions.
if len(newProject.Spec.SupportedRegions) == 0 || slices.Contains(newProject.Spec.SupportedRegions, apiv1.AllRegions) {
return nil
}

return nil
// If the user is removing a supported region, ensure that there are no apps in that region.
return v.ensureNoObjectsExistOutsideOfRegions(ctx, newProject.Name, newProject.Spec.SupportedRegions, &apiv1.AppList{}, &apiv1.VolumeList{})
}

func (v *Validator) ensureNoObjectsExistInRegions(ctx context.Context, namespace string, regions, removedRegions []string, objList ...kclient.ObjectList) field.ErrorList {
func (v *Validator) ensureNoObjectsExistOutsideOfRegions(ctx context.Context, namespace string, regions []string, objList ...kclient.ObjectList) field.ErrorList {
var result field.ErrorList
for _, obj := range objList {
var inRemovedRegion []string
var removedRegions, inRemovedRegion []string
if err := v.Client.List(ctx, obj, kclient.InNamespace(namespace)); err != nil {
return field.ErrorList{field.InternalError(field.NewPath("spec", "supportedRegions"), err)}
}

if err := meta.EachListItem(obj, func(object runtime.Object) error {
regionObject, ok := object.(regionNamer)
if ok && slices.Contains(removedRegions, regionObject.GetRegion()) {
if ok && !slices.Contains(regions, regionObject.GetRegion()) {
removedRegions = append(removedRegions, regionObject.GetRegion())
inRemovedRegion = append(inRemovedRegion, regionObject.GetName())
}
return nil
Expand Down
Loading