diff --git a/apiserver/organization/convert.go b/apiserver/organization/convert.go index 6e9dae5e..4e660df3 100644 --- a/apiserver/organization/convert.go +++ b/apiserver/organization/convert.go @@ -7,7 +7,6 @@ import ( orgv1 "github.com/appuio/control-api/apis/organization/v1" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var ( @@ -22,18 +21,21 @@ func namespaceToOrg(ns *corev1.Namespace) *orgv1.Organization { if ns.Annotations != nil { displayName = ns.Annotations[displayNameKey] } - return &orgv1.Organization{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespaceNameToOrgName(ns.Name), - CreationTimestamp: ns.CreationTimestamp, - Annotations: map[string]string{ - namespaceKey: ns.Name, - }, - }, + org := &orgv1.Organization{ + ObjectMeta: *ns.ObjectMeta.DeepCopy(), Spec: orgv1.OrganizationSpec{ DisplayName: displayName, }, } + org.Name = namespaceNameToOrgName(ns.Name) + if org.Annotations == nil { + org.Annotations = map[string]string{} + } + org.Annotations[namespaceKey] = ns.Name + delete(org.Annotations, displayNameKey) + delete(org.Labels, typeKey) + delete(org.Labels, nameKey) + return org } func namespaceNameToOrgName(ns string) string { @@ -41,18 +43,21 @@ func namespaceNameToOrgName(ns string) string { } func orgToNamespace(org *orgv1.Organization) *corev1.Namespace { - return &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: orgNameToNamespaceName(org.Name), - Labels: map[string]string{ - typeKey: "organization", - nameKey: org.Name, - }, - Annotations: map[string]string{ - displayNameKey: org.Spec.DisplayName, - }, - }, + ns := &corev1.Namespace{ + ObjectMeta: *org.ObjectMeta.DeepCopy(), + } + ns.Name = orgNameToNamespaceName(org.Name) + if ns.Labels == nil { + ns.Labels = map[string]string{} + } + + if ns.Annotations == nil { + ns.Annotations = map[string]string{} } + ns.Labels[typeKey] = "organization" + ns.Labels[nameKey] = org.Name + ns.Annotations[displayNameKey] = org.Spec.DisplayName + return ns } func orgNameToNamespaceName(org string) string { return fmt.Sprintf("org-%s", org) diff --git a/apiserver/organization/namespace.go b/apiserver/organization/namespace.go index 33042fb9..5b82cfac 100644 --- a/apiserver/organization/namespace.go +++ b/apiserver/organization/namespace.go @@ -43,6 +43,14 @@ func (p *loopbackNamespaceProvider) createNamespace(ctx context.Context, ns *cor return p.client.Create(ctx, ns) } +func (p *loopbackNamespaceProvider) updateNamespace(ctx context.Context, ns *corev1.Namespace) error { + err := p.init() + if err != nil { + return err + } + return p.client.Update(ctx, ns) +} + func (p *loopbackNamespaceProvider) listNamespaces(ctx context.Context) (*corev1.NamespaceList, error) { err := p.init() if err != nil { diff --git a/apiserver/organization/organization.go b/apiserver/organization/organization.go index cdc25a3c..e3173bb4 100644 --- a/apiserver/organization/organization.go +++ b/apiserver/organization/organization.go @@ -3,6 +3,7 @@ package organization import ( "context" "fmt" + "log" "time" orgv1 "github.com/appuio/control-api/apis/organization/v1" @@ -38,6 +39,7 @@ type organizationStorage struct { type namespaceProvider interface { getNamespace(ctx context.Context, name string) (*corev1.Namespace, error) createNamespace(ctx context.Context, ns *corev1.Namespace) error + updateNamespace(ctx context.Context, ns *corev1.Namespace) error listNamespaces(ctx context.Context) (*corev1.NamespaceList, error) } @@ -134,6 +136,59 @@ func (s *organizationStorage) List(ctx context.Context, options *metainternalver return &res, nil } +var _ rest.Updater = &organizationStorage{} +var _ rest.CreaterUpdater = &organizationStorage{} + +func (s *organizationStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, + createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, + forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { + + //TODO(glrf) Don't ignore update options + log.Println("Update") + + newOrg := &orgv1.Organization{} + + a, err := filters.GetAuthorizerAttributes(ctx) + if err != nil { + return nil, false, err + } + + decision, _, err := s.namespaceAuthorizer.Authorize(ctx, a) + if err != nil { + return nil, false, err + } else if decision != authorizer.DecisionAllow { + return nil, false, kerrors.NewNotFound(newOrg.GetGroupVersionResource().GroupResource(), name) + } + + log.Println("Update authorized") + oldOrg, err := s.Get(ctx, name, nil) + if err != nil { + + log.Printf("unable to get organization: %s\n", err) + return nil, false, fmt.Errorf("unable to get organization: %w", err) + } + + newObj, err := objInfo.UpdatedObject(ctx, oldOrg) + if err != nil { + log.Printf("unable to update org: %s\n", err) + return nil, false, err + } + + newOrg, ok := newObj.(*orgv1.Organization) + if !ok { + return nil, false, fmt.Errorf("new object is not an organization") + } + + if updateValidation != nil { + err = updateValidation(ctx, newOrg, oldOrg) + if err != nil { + return nil, false, err + } + } + log.Printf("Validated and updating") + + return newOrg, false, s.namepaces.updateNamespace(ctx, orgToNamespace(newOrg)) +} func (s *organizationStorage) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { var table metav1.Table diff --git a/main.go b/main.go index b8bcf56b..fb67615a 100644 --- a/main.go +++ b/main.go @@ -34,6 +34,7 @@ func main() { "uid", os.Getuid(), "gid", os.Getgid(), ).Info("Starting control-apiā€¦") + err := builder.APIServer. WithResourceAndHandler(&orgv1.Organization{}, orgStore.New()). WithLocalDebugExtension().