Skip to content

Commit c330fbd

Browse files
authored
Merge pull request kubernetes-retired#7 from brahmaroutu/controller_code
Adding bucketrequest controller (WIP)
2 parents 87c7a4f + 0078523 commit c330fbd

File tree

9 files changed

+1615
-2
lines changed

9 files changed

+1615
-2
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
.DS_Store
33
.build
44
*.swp
5+
travis.yml
56
release-tools
7+
bin

Diff for: .prow.sh

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#! /bin/bash
2+
3+
. release-tools/prow.sh
4+
5+
main
6+

Diff for: Makefile

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
#CMDS=cosi-controller-manager
1615
all: reltools build
1716
.PHONY: reltools
1817
reltools: release-tools/build.make
1918
release-tools/build.make:
2019
$(eval CURDIR := $(shell pwd))
2120
$(eval TMP := $(shell mktemp -d))
22-
$(shell cd ${TMP} && git clone git@github.com:kubernetes-sigs/container-object-storage-interface-spec.git)
21+
$(shell cd ${TMP} && git clone https://github.com/kubernetes-sigs/container-object-storage-interface-spec)
2322
$(shell cp -r ${TMP}/container-object-storage-interface-spec/release-tools ${CURDIR}/)
2423
$(shell rm -rf ${TMP})
24+
ln -s release-tools/travis.yml travis.yml
25+
26+
CMDS=controller-manager
2527

2628
include release-tools/build.make

Diff for: cmd/controller-manager/controller-manager.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"os"
7+
"os/signal"
8+
"syscall"
9+
10+
"github.com/spf13/cobra"
11+
"github.com/spf13/viper"
12+
13+
bucketcontroller "github.com/kubernetes-sigs/container-object-storage-interface-api/controller"
14+
"github.com/kubernetes-sigs/container-object-storage-interface-controller/pkg/bucketrequest"
15+
16+
"github.com/golang/glog"
17+
)
18+
19+
var cmd = &cobra.Command{
20+
Use: "controller-manager",
21+
Short: "central controller for managing bucket* and bucketAccess* API objects",
22+
SilenceErrors: true,
23+
SilenceUsage: true,
24+
RunE: func(c *cobra.Command, args []string) error {
25+
return run(c.Context(), args)
26+
},
27+
DisableFlagsInUseLine: true,
28+
}
29+
30+
var kubeConfig string
31+
32+
func init() {
33+
viper.AutomaticEnv()
34+
35+
cmd.PersistentFlags().AddGoFlagSet(flag.CommandLine)
36+
flag.Set("logtostderr", "true")
37+
38+
strFlag := func(c *cobra.Command, ptr *string, name string, short string, dfault string, desc string) {
39+
c.PersistentFlags().
40+
StringVarP(ptr, name, short, dfault, desc)
41+
}
42+
strFlag(cmd, &kubeConfig, "kube-config", "", kubeConfig, "path to kubeconfig file")
43+
44+
hideFlag := func(name string) {
45+
cmd.PersistentFlags().MarkHidden(name)
46+
}
47+
hideFlag("alsologtostderr")
48+
hideFlag("log_backtrace_at")
49+
hideFlag("log_dir")
50+
hideFlag("logtostderr")
51+
hideFlag("master")
52+
hideFlag("stderrthreshold")
53+
hideFlag("vmodule")
54+
55+
// suppress the incorrect prefix in glog output
56+
flag.CommandLine.Parse([]string{})
57+
viper.BindPFlags(cmd.PersistentFlags())
58+
59+
}
60+
61+
func main() {
62+
if err := cmd.Execute(); err != nil {
63+
glog.Fatal(err.Error())
64+
}
65+
66+
var cancel context.CancelFunc
67+
68+
_, cancel = context.WithCancel(cmd.Context())
69+
sigs := make(chan os.Signal, 1)
70+
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
71+
72+
go func() {
73+
<-sigs
74+
cancel()
75+
}()
76+
}
77+
78+
func run(ctx context.Context, args []string) error {
79+
ctrl, err := bucketcontroller.NewDefaultObjectStorageController("controller-manager", "leader-lock", 40)
80+
if err != nil {
81+
return err
82+
}
83+
ctrl.AddBucketRequestListener(bucketrequest.NewListener())
84+
return ctrl.Run(ctx)
85+
}

Diff for: go.mod

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module github.com/kubernetes-sigs/container-object-storage-interface-controller
2+
3+
go 1.15
4+
5+
require (
6+
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
7+
github.com/kubernetes-sigs/container-object-storage-interface-api v0.0.0-20201204201926-43539346a903
8+
github.com/spf13/cobra v1.1.1
9+
github.com/spf13/viper v1.7.1
10+
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
11+
k8s.io/apiextensions-apiserver v0.19.4
12+
k8s.io/apimachinery v0.19.4
13+
k8s.io/client-go v0.19.4
14+
sigs.k8s.io/controller-tools v0.4.1
15+
)

Diff for: go.sum

+750
Large diffs are not rendered by default.

Diff for: pkg/bucketrequest/bucketrequest.go

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package bucketrequest
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
10+
"github.com/kubernetes-sigs/container-object-storage-interface-api/apis/objectstorage.k8s.io/v1alpha1"
11+
bucketclientset "github.com/kubernetes-sigs/container-object-storage-interface-api/clientset"
12+
bucketcontroller "github.com/kubernetes-sigs/container-object-storage-interface-api/controller"
13+
"github.com/kubernetes-sigs/container-object-storage-interface-controller/pkg/util"
14+
kubeclientset "k8s.io/client-go/kubernetes"
15+
16+
"github.com/golang/glog"
17+
)
18+
19+
type bucketRequestListener struct {
20+
kubeClient kubeclientset.Interface
21+
bucketClient bucketclientset.Interface
22+
}
23+
24+
func NewListener() bucketcontroller.BucketRequestListener {
25+
return &bucketRequestListener{}
26+
}
27+
28+
func (b *bucketRequestListener) InitializeKubeClient(k kubeclientset.Interface) {
29+
b.kubeClient = k
30+
}
31+
32+
func (b *bucketRequestListener) InitializeBucketClient(bc bucketclientset.Interface) {
33+
b.bucketClient = bc
34+
}
35+
36+
// Add creates a bucket in response to a bucketrequest
37+
func (b *bucketRequestListener) Add(ctx context.Context, obj *v1alpha1.BucketRequest) error {
38+
glog.V(1).Infof("add called for bucket %s", obj.Name)
39+
bucketRequest := obj
40+
err := b.provisionBucketRequestOperation(ctx, bucketRequest)
41+
if err != nil {
42+
// Provisioning is 100% finished / not in progress.
43+
switch err {
44+
case util.ErrInvalidBucketClass:
45+
glog.V(5).Infof("Bucket Class specified does not exist. Stop provisioning, removing bucketRequest %s from bucketRequests in progress", bucketRequest.UID)
46+
err = nil
47+
case util.ErrBucketAlreadyExists:
48+
glog.V(5).Infof("Bucket already exist for this bucket request. Stop provisioning, removing bucketRequest %s from bucketRequests in progress", bucketRequest.UID)
49+
err = nil
50+
default:
51+
glog.V(2).Infof("Final error received, removing buckerRequest %s from bucketRequests in progress", bucketRequest.UID)
52+
}
53+
return err
54+
}
55+
56+
glog.V(5).Infof("BucketRequest processing succeeded, removing bucketRequest %s from bucketRequests in progress", bucketRequest.UID)
57+
return nil
58+
}
59+
60+
// update processes any updates made to the bucket request
61+
func (b *bucketRequestListener) Update(ctx context.Context, old, new *v1alpha1.BucketRequest) error {
62+
glog.V(1).Infof("update called for bucket %v", old)
63+
return nil
64+
}
65+
66+
// Delete processes a bucket for which bucket request is deleted
67+
func (b *bucketRequestListener) Delete(ctx context.Context, obj *v1alpha1.BucketRequest) error {
68+
return nil
69+
}
70+
71+
// provisionBucketRequestOperation attempts to provision a bucket for the given bucketRequest.
72+
// Returns nil error only when the bucket was provisioned, an error it set appropriately if not.
73+
// Returns a normal error when the bucket was not provisioned and provisioning should be retried (requeue the bucketRequest),
74+
// or the special error errBucketAlreadyExists, errInvalidBucketClass, when provisioning was impossible and
75+
// no further attempts to provision should be tried.
76+
func (b *bucketRequestListener) provisionBucketRequestOperation(ctx context.Context, bucketRequest *v1alpha1.BucketRequest) error {
77+
// Most code here is identical to that found in controller.go of kube's controller...
78+
bucketClassName := b.GetBucketClass(bucketRequest)
79+
80+
// A previous doProvisionBucketRequest may just have finished while we were waiting for
81+
// the locks. Check that bucket (with deterministic name) hasn't been provisioned
82+
// yet.
83+
bucket := b.FindBucket(ctx, bucketRequest)
84+
if bucket != nil {
85+
// bucket has been already provisioned, nothing to do.
86+
glog.Info("Bucket already exists", bucket.Name)
87+
return util.ErrBucketAlreadyExists
88+
}
89+
90+
bucketClass, err := b.bucketClient.ObjectstorageV1alpha1().BucketClasses().Get(ctx, bucketClassName, metav1.GetOptions{})
91+
if bucketClass == nil {
92+
// bucketclass does not exist in order to create a bucket
93+
return util.ErrInvalidBucketClass
94+
}
95+
96+
glog.Infof("creating bucket for bucketrequest %v", bucketRequest.Name)
97+
98+
// create bucket
99+
bucket = &v1alpha1.Bucket{}
100+
bucket.Name = fmt.Sprintf("%s%s", bucketRequest.Spec.BucketPrefix, util.GetUUID())
101+
bucket.Spec.Provisioner = bucketClass.Provisioner
102+
bucket.Spec.RetentionPolicy = bucketClass.RetentionPolicy
103+
bucket.Spec.AnonymousAccessMode = bucketClass.AnonymousAccessMode
104+
bucket.Spec.BucketClassName = bucketClass.Name
105+
bucket.Spec.BucketRequest = &v1alpha1.BucketRequestReference{
106+
Name: bucketRequest.Name,
107+
Namespace: bucketRequest.Namespace,
108+
UID: bucketRequest.ObjectMeta.UID}
109+
bucket.Spec.AllowedNamespaces = util.CopyStrings(bucketClass.AllowedNamespaces)
110+
bucket.Spec.Parameters = util.CopySS(bucketClass.Parameters)
111+
112+
// TODO have a switch statement to populate appropriate protocol based on BR.Protocol
113+
bucket.Spec.Protocol.RequestedProtocol = bucketRequest.Spec.Protocol
114+
115+
bucket, err = b.bucketClient.ObjectstorageV1alpha1().Buckets().Create(context.Background(), bucket, metav1.CreateOptions{})
116+
if err != nil {
117+
glog.V(5).Infof("Error occurred when creating bucket %v", err)
118+
return err
119+
}
120+
121+
glog.Infof("Finished creating bucket %v", bucket.Name)
122+
return nil
123+
}
124+
125+
// GetBucketClass returns BucketClassName. If no bucket class was in the request it returns empty
126+
// TODO this methods can be more sophisticate to address bucketClass overrides using annotations just like SC.
127+
func (b *bucketRequestListener) GetBucketClass(bucketRequest *v1alpha1.BucketRequest) string {
128+
129+
if bucketRequest.Spec.BucketClassName != "" {
130+
return bucketRequest.Spec.BucketClassName
131+
}
132+
133+
return ""
134+
}
135+
136+
func (b *bucketRequestListener) FindBucket(ctx context.Context, br *v1alpha1.BucketRequest) *v1alpha1.Bucket {
137+
bucketList, err := b.bucketClient.ObjectstorageV1alpha1().Buckets().List(ctx, metav1.ListOptions{})
138+
if err != nil {
139+
return nil
140+
}
141+
if len(bucketList.Items) > 0 {
142+
for _, bucket := range bucketList.Items {
143+
if strings.HasPrefix(bucket.Name, br.Spec.BucketPrefix) &&
144+
bucket.Spec.BucketClassName == br.Spec.BucketClassName &&
145+
bucket.Spec.BucketRequest.Name == br.Name &&
146+
bucket.Spec.BucketRequest.Namespace == br.Namespace &&
147+
bucket.Spec.BucketRequest.UID == br.ObjectMeta.UID {
148+
return &bucket
149+
}
150+
}
151+
}
152+
return nil
153+
}
154+
155+
// cloneTheBucket clones a bucket to a different namespace when a BR is for brownfield.
156+
func (b *bucketRequestListener) cloneTheBucket(bucketRequest *v1alpha1.BucketRequest) error {
157+
glog.V(1).Infof("clone called for bucket %s", bucketRequest.Spec.BucketInstanceName)
158+
return util.ErrNotImplemented
159+
}
160+
161+
// logOperation format and prints logs
162+
func logOperation(operation, format string, a ...interface{}) string {
163+
return fmt.Sprintf(fmt.Sprintf("%s: %s", operation, format), a...)
164+
}

0 commit comments

Comments
 (0)