-
Notifications
You must be signed in to change notification settings - Fork 0
/
namespace.go
135 lines (120 loc) · 3.96 KB
/
namespace.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package namespace
import (
"time"
"github.com/pkg/errors"
"github.com/rancher/norman/api/access"
"github.com/rancher/norman/httperror"
"github.com/rancher/norman/parse"
"github.com/rancher/norman/types"
"github.com/rancher/norman/types/convert"
"github.com/rancher/rancher/pkg/clustermanager"
"github.com/rancher/rancher/pkg/controllers/user/helm"
"github.com/rancher/rancher/pkg/rbac"
"github.com/rancher/rancher/pkg/ref"
"github.com/rancher/types/apis/cluster.cattle.io/v3/schema"
client "github.com/rancher/types/client/cluster/v3"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/cache"
)
var (
projectIDFieldLabel = "field.cattle.io/projectId"
namespaceOwnerMap = cache.NewLRUExpireCache(1000)
)
func updateNamespaceOwnerMap(apiContext *types.APIContext) error {
var namespaces []client.Namespace
if err := access.List(apiContext, &schema.Version, client.NamespaceType, &types.QueryOptions{}, &namespaces); err != nil {
return err
}
for _, namespace := range namespaces {
namespaceOwnerMap.Add(namespace.Name, namespace.ProjectID, time.Hour)
}
return nil
}
func ProjectMap(apiContext *types.APIContext, refresh bool) (map[string]string, error) {
if refresh {
err := updateNamespaceOwnerMap(apiContext)
if err != nil {
return nil, err
}
}
data := map[string]string{}
for _, key := range namespaceOwnerMap.Keys() {
if val, ok := namespaceOwnerMap.Get(key); ok {
data[key.(string)] = val.(string)
}
}
return data, nil
}
type ActionWrapper struct {
ClusterManager *clustermanager.Manager
}
func (w ActionWrapper) ActionHandler(actionName string, action *types.Action, apiContext *types.APIContext) error {
actionInput, err := parse.ReadBody(apiContext.Request)
if err != nil {
return err
}
if !canUpdateNS(apiContext, nil) {
return httperror.NewAPIError(httperror.NotFound, "not found")
}
switch actionName {
case "move":
clusterID := w.ClusterManager.ClusterName(apiContext)
_, projectID := ref.Parse(convert.ToString(actionInput["projectId"]))
userContext, err := w.ClusterManager.UserContext(clusterID)
if err != nil {
if !kerrors.IsNotFound(err) {
return err
}
return httperror.NewAPIError(httperror.NotFound, err.Error())
}
if projectID != "" {
project, err := userContext.Management.Management.Projects(clusterID).Get(projectID, metav1.GetOptions{})
if err != nil {
return err
}
if project.Spec.ResourceQuota != nil {
return errors.Errorf("can't move namespace. Project %s has resource quota set", project.Spec.DisplayName)
}
}
nsClient := userContext.Core.Namespaces("")
ns, err := nsClient.Get(apiContext.ID, metav1.GetOptions{})
if err != nil {
if !kerrors.IsNotFound(err) {
return err
}
return httperror.NewAPIError(httperror.NotFound, err.Error())
}
if ns.Annotations[helm.AppIDsLabel] != "" {
return errors.New("namespace is currently being used")
}
if projectID == "" {
delete(ns.Annotations, projectIDFieldLabel)
} else {
ns.Annotations[projectIDFieldLabel] = convert.ToString(actionInput["projectId"])
}
if _, err := nsClient.Update(ns); err != nil {
return err
}
default:
return errors.New("invalid action")
}
return nil
}
func NewFormatter(next types.Formatter) types.Formatter {
return func(request *types.APIContext, resource *types.RawResource) {
if next != nil {
next(request, resource)
}
annotations := convert.ToMapInterface(resource.Values["annotations"])
canUpdate := canUpdateNS(request, resource)
if canUpdate && convert.ToString(annotations[helm.AppIDsLabel]) == "" {
resource.AddAction(request, "move")
}
}
}
func canUpdateNS(apiContext *types.APIContext, resource *types.RawResource) bool {
obj := rbac.ObjFromContext(apiContext, resource)
// the user must have * permissions on namespace, the create-ns role alone won't return true here
return apiContext.AccessControl.CanDo("", "namespaces", "update", apiContext, obj, apiContext.Schema) == nil
}