This repository was archived by the owner on Apr 1, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 244
Expand file tree
/
Copy pathnamespace.go
More file actions
214 lines (184 loc) · 6.07 KB
/
namespace.go
File metadata and controls
214 lines (184 loc) · 6.07 KB
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package kclient
import (
"context"
"errors"
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"
"k8s.io/pod-security-admission/api"
psaApi "k8s.io/pod-security-admission/api"
)
const (
// timeout for waiting for namespace deletion
waitForNamespaceDeletionTimeOut = 3 * time.Minute
// timeout for getting the default service account
getDefaultServiceAccTimeout = 1 * time.Minute
)
// GetNamespaces return list of existing namespaces that user has access to.
func (c *Client) GetNamespaces() ([]string, error) {
namespaces, err := c.KubeClient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("unable to list namespaces: %w", err)
}
var names []string
for _, p := range namespaces.Items {
names = append(names, p.Name)
}
return names, nil
}
// GetNamespace returns Namespace based on its name
// Errors related to project not being found or forbidden are translated to nil project for compatibility
func (c *Client) GetNamespace(name string) (*corev1.Namespace, error) {
ns, err := c.KubeClient.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
istatus, ok := err.(kerrors.APIStatus)
if ok {
status := istatus.Status()
if status.Reason == metav1.StatusReasonNotFound || status.Reason == metav1.StatusReasonForbidden {
return nil, nil
}
} else {
return nil, err
}
}
return ns, err
}
// GetNamespace returns Namespace based on its name
func (c *Client) GetNamespaceNormal(name string) (*corev1.Namespace, error) {
return c.KubeClient.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{})
}
// CreateNamespace creates new namespace
func (c *Client) CreateNamespace(name string) (*corev1.Namespace, error) {
namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
}
newNamespace, err := c.KubeClient.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return nil, fmt.Errorf("unable to create Namespace %s: %w", namespace.ObjectMeta.Name, err)
}
return newNamespace, nil
}
// DeleteNamespace deletes namespace
// if wait=true , it will wait for deletion
func (c *Client) DeleteNamespace(name string, wait bool) error {
var watcher watch.Interface
var err error
if wait {
watcher, err = c.KubeClient.CoreV1().Namespaces().Watch(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.name": name}.AsSelector().String(),
})
if err != nil {
return fmt.Errorf("unable to watch namespace: %w", err)
}
defer watcher.Stop()
}
err = c.KubeClient.CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
return fmt.Errorf("unable to delete Namespace %s: %w", name, err)
}
if watcher != nil {
namespaceChannel := make(chan *corev1.Namespace)
watchErrorChannel := make(chan error)
go func() {
for {
val, ok := <-watcher.ResultChan()
if !ok {
watchErrorChannel <- fmt.Errorf("watch channel was closed unexpectedly: %+v", val)
break
}
klog.V(3).Infof("Watch event.Type '%s'.", val.Type)
if namespaceStatus, ok := val.Object.(*corev1.Namespace); ok {
klog.V(3).Infof("Status of delete of namespace %s is '%s'.", name, namespaceStatus.Status.Phase)
if val.Type == watch.Deleted {
namespaceChannel <- namespaceStatus
break
}
if val.Type == watch.Error {
watchErrorChannel <- fmt.Errorf("failed watching the deletion of namespace %s", name)
break
}
}
}
close(namespaceChannel)
close(watchErrorChannel)
}()
select {
case val := <-namespaceChannel:
klog.V(3).Infof("Namespace %s deleted", val.Name)
return nil
case err := <-watchErrorChannel:
return err
case <-time.After(waitForNamespaceDeletionTimeOut):
return fmt.Errorf("waited %s but couldn't delete namespace %s in time", waitForNamespaceDeletionTimeOut, name)
}
}
return nil
}
// SetCurrentNamespace change current namespace in kubeconfig
func (c *Client) SetCurrentNamespace(namespace string) error {
rawConfig, err := c.KubeConfig.RawConfig()
if err != nil {
return fmt.Errorf("unable to switch to %s project: %w", namespace, err)
}
rawConfig.Contexts[rawConfig.CurrentContext].Namespace = namespace
err = clientcmd.ModifyConfig(clientcmd.NewDefaultClientConfigLoadingRules(), rawConfig, true)
if err != nil {
return fmt.Errorf("unable to switch to %s project: %w", namespace, err)
}
c.Namespace = namespace
return nil
}
func (c *Client) GetCurrentNamespace() string {
return c.Namespace
}
func (c *Client) SetNamespace(ns string) {
c.Namespace = ns
}
// WaitForServiceAccountInNamespace waits for the given service account to be ready
func (c *Client) WaitForServiceAccountInNamespace(namespace, serviceAccountName string) error {
if namespace == "" || serviceAccountName == "" {
return errors.New("namespace and serviceAccountName cannot be empty")
}
watcher, err := c.KubeClient.CoreV1().ServiceAccounts(namespace).Watch(context.TODO(), metav1.SingleObject(metav1.ObjectMeta{Name: serviceAccountName}))
if err != nil {
return err
}
timeout := time.After(getDefaultServiceAccTimeout)
if watcher != nil {
defer watcher.Stop()
for {
select {
case val, ok := <-watcher.ResultChan():
if !ok {
break
}
if serviceAccount, ok := val.Object.(*corev1.ServiceAccount); ok {
if serviceAccount.Name == serviceAccountName {
klog.V(3).Infof("Status of creation of service account %s is ready", serviceAccount)
return nil
}
}
case <-timeout:
return errors.New("timed out waiting for service to be ready")
}
}
}
return nil
}
func (c *Client) GetCurrentNamespacePolicy() (psaApi.Policy, error) {
ns, err := c.GetNamespaceNormal(c.GetCurrentNamespace())
if err != nil {
return psaApi.Policy{}, err
}
labels := ns.GetLabels()
policy, _ := api.PolicyToEvaluate(labels, api.Policy{})
return policy, nil
}