forked from rook/rook
ceph: Add CRD for Object Store User
Add the ability to create and manage object store users with CRDs Signed-off-by: Ben Zieglmeier <benjamin.zieglmeier@target.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Ceph Shared File System
|
||||
weight: 35
|
||||
weight: 36
|
||||
indent: true
|
||||
---
|
||||
{% assign url = page.url | split: '/' %}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Ceph Object Store User
|
||||
weight: 35
|
||||
indent: true
|
||||
---
|
||||
|
||||
# Ceph Object Store User CRD
|
||||
|
||||
Rook allows creation and customization of object store users through the custom resource definitions (CRDs). The following settings are available
|
||||
for Ceph object store users.
|
||||
|
||||
## Sample
|
||||
|
||||
```yaml
|
||||
apiVersion: ceph.rook.io/v1beta1
|
||||
kind: ObjectStoreUser
|
||||
metadata:
|
||||
name: my-user
|
||||
namespace: rook-ceph
|
||||
spec:
|
||||
store: my-store
|
||||
displayName: my-display-name
|
||||
```
|
||||
|
||||
## Object Store User Settings
|
||||
|
||||
### Metadata
|
||||
|
||||
- `name`: The name of the object store user to create, which will be reflected in the secret and other resource names.
|
||||
- `namespace`: The namespace of the Rook cluster where the object store user is created.
|
||||
|
||||
### Spec
|
||||
|
||||
- `store`: The object store in which the user will be created. This matches the name of the objectstore CRD.
|
||||
- `displayName`: The display name which will be passed to the `radosgw-admin user create` command.
|
||||
+36
-10
@@ -51,20 +51,46 @@ kubectl -n rook-ceph get pod -l app=rook-ceph-rgw
|
||||
|
||||
## Create a User
|
||||
|
||||
Creating an object storage user requires running a `radosgw-admin` command with the [Rook toolbox](ceph-quickstart.md#tools) pod. This will be simplified in the future with a CRD for the object store users.
|
||||
Next we will create the object store user, which calls the RGW service in the cluster with the S3 API.
|
||||
Specify your desired settings for the object store user in the `object-user.yaml`. For more details on the settings see the [Object Store User CRD](ceph-object-store-user-crd.md).
|
||||
|
||||
```yaml
|
||||
apiVersion: ceph.rook.io/v1beta1
|
||||
kind: ObjectStoreUser
|
||||
metadata:
|
||||
name: my-user
|
||||
namespace: rook-ceph
|
||||
spec:
|
||||
store: my-store
|
||||
displayName: "my display name"
|
||||
```
|
||||
|
||||
When the object store user is created the Rook operator will create the RGW user on the object store specified, and store the Access Key and Secret Key in a kubernetes secret in the same namespace as the object store user.
|
||||
|
||||
```bash
|
||||
radosgw-admin user create --uid rook-user --display-name "A rook rgw User" --rgw-realm=my-store --rgw-zonegroup=my-store
|
||||
# Create the object store user
|
||||
kubectl create -f object-user.yaml
|
||||
|
||||
# To confirm the object store user is configured, describe the secret
|
||||
kubectl -n rook-ceph describe secret rook-ceph-object-user-my-user
|
||||
|
||||
Name: rook-ceph-object-user-my-user
|
||||
Namespace: rook-ceph
|
||||
Labels: app=rook-ceph-rgw
|
||||
rook_cluster=rook-ceph
|
||||
rook_object_store=my-store
|
||||
Annotations: <none>
|
||||
|
||||
Type: kubernetes.io/rook
|
||||
|
||||
Data
|
||||
====
|
||||
AccessKey: 20 bytes
|
||||
SecretKey: 40 bytes
|
||||
```
|
||||
|
||||
The object store is now available by using the creds of `rook-user`. Take note of the `access_key` and `secret_key` printed by the user creation. For example:
|
||||
```json
|
||||
{
|
||||
"user": "rook-user",
|
||||
"access_key": "XEZDB3UJ6X7HVBE7X7MA",
|
||||
"secret_key": "7yGIZON7EhFORz0I40BFniML36D2rl8CQQ5kXU6l"
|
||||
}
|
||||
```
|
||||
The AccessKey and SecretKey data fields can be mounted in a pod as an environment variable. More information on consuming
|
||||
kubernetes secrets can be found on [The kubernetes website](https://kubernetes.io/docs/concepts/configuration/secret/)
|
||||
|
||||
## Consume the Object Storage
|
||||
|
||||
|
||||
Generated
+10
-11
@@ -35,7 +35,7 @@
|
||||
"private/protocol/restxml",
|
||||
"private/protocol/xml/xmlutil",
|
||||
"service/s3",
|
||||
"service/sts",
|
||||
"service/sts"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "a8ff9e4804fc89c994b731b1c057640ab2aecff3"
|
||||
@@ -128,7 +128,7 @@
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"sortkeys",
|
||||
"sortkeys"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
|
||||
@@ -158,7 +158,7 @@
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/timestamp",
|
||||
"ptypes/timestamp"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
|
||||
@@ -194,7 +194,7 @@
|
||||
packages = [
|
||||
"OpenAPIv2",
|
||||
"compiler",
|
||||
"extensions",
|
||||
"extensions"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
|
||||
@@ -206,7 +206,7 @@
|
||||
name = "github.com/gregjones/httpcache"
|
||||
packages = [
|
||||
".",
|
||||
"diskcache",
|
||||
"diskcache"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "9cad4c3443a7200dd6400aef47183728de563a38"
|
||||
@@ -216,7 +216,7 @@
|
||||
name = "github.com/hashicorp/golang-lru"
|
||||
packages = [
|
||||
".",
|
||||
"simplelru",
|
||||
"simplelru"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768"
|
||||
@@ -231,7 +231,6 @@
|
||||
revision = "4178557ae428460c3780a381c824a1f3aceb6325"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
@@ -348,7 +347,7 @@
|
||||
packages = [
|
||||
"expfmt",
|
||||
"internal/bitbucket.org/ww/goautoneg",
|
||||
"model",
|
||||
"model"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
|
||||
@@ -407,7 +406,7 @@
|
||||
packages = [
|
||||
"assert",
|
||||
"require",
|
||||
"suite",
|
||||
"suite"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
||||
@@ -632,7 +631,7 @@
|
||||
"pkg/version",
|
||||
"pkg/watch",
|
||||
"third_party/forked/golang/json",
|
||||
"third_party/forked/golang/reflect",
|
||||
"third_party/forked/golang/reflect"
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "def12e63c512da17043b4f0293f52d1006603d9f"
|
||||
@@ -839,7 +838,7 @@
|
||||
"generator",
|
||||
"namer",
|
||||
"parser",
|
||||
"types",
|
||||
"types"
|
||||
]
|
||||
pruneopts = "T"
|
||||
revision = "4242d8e6c5dba56827bb7bcf14ad11cda38f3991"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
- The toolbox manifest now creates a deployment based on the `rook/ceph` image instead of creating a pod on a specialized `rook/ceph-toolbox` image.
|
||||
- The frequency of discovering devices on a node is reduced to 60 minutes by default, and is configurable with the setting `ROOK_DISCOVER_DEVICES_INTERVAL` in operator.yaml.
|
||||
- The number of mons can be changed by updating the `mon.count` in the cluster CRD.
|
||||
- Object Store User creation via CRD for Ceph clusters.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: ceph.rook.io/v1beta1
|
||||
kind: ObjectStoreUser
|
||||
metadata:
|
||||
name: my-user
|
||||
namespace: rook-ceph
|
||||
spec:
|
||||
store: my-store
|
||||
displayName: "my display name"
|
||||
@@ -100,6 +100,22 @@ spec:
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: objectstoreusers.ceph.rook.io
|
||||
spec:
|
||||
group: ceph.rook.io
|
||||
names:
|
||||
kind: ObjectStoreUser
|
||||
listKind: ObjectStoreUserList
|
||||
plural: objectstoreusers
|
||||
singular: objectstoreuser
|
||||
shortNames:
|
||||
- rcou
|
||||
scope: Namespaced
|
||||
version: v1beta1
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: pools.ceph.rook.io
|
||||
spec:
|
||||
|
||||
@@ -61,6 +61,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&FilesystemList{},
|
||||
&ObjectStore{},
|
||||
&ObjectStoreList{},
|
||||
&ObjectStoreUser{},
|
||||
&ObjectStoreUserList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
|
||||
@@ -241,6 +241,32 @@ type ObjectStoreSpec struct {
|
||||
Gateway GatewaySpec `json:"gateway"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +genclient:noStatus
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type ObjectStoreUser struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata"`
|
||||
Spec ObjectStoreUserSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type ObjectStoreUserList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata"`
|
||||
Items []ObjectStoreUser `json:"items"`
|
||||
}
|
||||
|
||||
// ObjectStoreUserSpec represent the spec of an Objectstoreuser
|
||||
type ObjectStoreUserSpec struct {
|
||||
//The store the user will be created in
|
||||
Store string `json:"store,omitempty"`
|
||||
//The display name for the ceph users
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
}
|
||||
|
||||
type GatewaySpec struct {
|
||||
// The port the rgw service will be listening on (http)
|
||||
Port int32 `json:"port"`
|
||||
|
||||
@@ -399,6 +399,84 @@ func (in *ObjectStoreSpec) DeepCopy() *ObjectStoreSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ObjectStoreUser) DeepCopyInto(out *ObjectStoreUser) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStoreUser.
|
||||
func (in *ObjectStoreUser) DeepCopy() *ObjectStoreUser {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ObjectStoreUser)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ObjectStoreUser) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ObjectStoreUserList) DeepCopyInto(out *ObjectStoreUserList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
out.ListMeta = in.ListMeta
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ObjectStoreUser, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStoreUserList.
|
||||
func (in *ObjectStoreUserList) DeepCopy() *ObjectStoreUserList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ObjectStoreUserList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ObjectStoreUserList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ObjectStoreUserSpec) DeepCopyInto(out *ObjectStoreUserSpec) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStoreUserSpec.
|
||||
func (in *ObjectStoreUserSpec) DeepCopy() *ObjectStoreUserSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ObjectStoreUserSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Pool) DeepCopyInto(out *Pool) {
|
||||
*out = *in
|
||||
|
||||
@@ -30,6 +30,7 @@ type CephV1beta1Interface interface {
|
||||
ClustersGetter
|
||||
FilesystemsGetter
|
||||
ObjectStoresGetter
|
||||
ObjectStoreUsersGetter
|
||||
PoolsGetter
|
||||
}
|
||||
|
||||
@@ -50,6 +51,10 @@ func (c *CephV1beta1Client) ObjectStores(namespace string) ObjectStoreInterface
|
||||
return newObjectStores(c, namespace)
|
||||
}
|
||||
|
||||
func (c *CephV1beta1Client) ObjectStoreUsers(namespace string) ObjectStoreUserInterface {
|
||||
return newObjectStoreUsers(c, namespace)
|
||||
}
|
||||
|
||||
func (c *CephV1beta1Client) Pools(namespace string) PoolInterface {
|
||||
return newPools(c, namespace)
|
||||
}
|
||||
|
||||
+4
@@ -40,6 +40,10 @@ func (c *FakeCephV1beta1) ObjectStores(namespace string) v1beta1.ObjectStoreInte
|
||||
return &FakeObjectStores{c, namespace}
|
||||
}
|
||||
|
||||
func (c *FakeCephV1beta1) ObjectStoreUsers(namespace string) v1beta1.ObjectStoreUserInterface {
|
||||
return &FakeObjectStoreUsers{c, namespace}
|
||||
}
|
||||
|
||||
func (c *FakeCephV1beta1) Pools(namespace string) v1beta1.PoolInterface {
|
||||
return &FakePools{c, namespace}
|
||||
}
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
v1beta1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
// FakeObjectStoreUsers implements ObjectStoreUserInterface
|
||||
type FakeObjectStoreUsers struct {
|
||||
Fake *FakeCephV1beta1
|
||||
ns string
|
||||
}
|
||||
|
||||
var objectstoreusersResource = schema.GroupVersionResource{Group: "ceph.rook.io", Version: "v1beta1", Resource: "objectstoreusers"}
|
||||
|
||||
var objectstoreusersKind = schema.GroupVersionKind{Group: "ceph.rook.io", Version: "v1beta1", Kind: "ObjectStoreUser"}
|
||||
|
||||
// Get takes name of the objectStoreUser, and returns the corresponding objectStoreUser object, and an error if there is any.
|
||||
func (c *FakeObjectStoreUsers) Get(name string, options v1.GetOptions) (result *v1beta1.ObjectStoreUser, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(objectstoreusersResource, c.ns, name), &v1beta1.ObjectStoreUser{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ObjectStoreUser), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ObjectStoreUsers that match those selectors.
|
||||
func (c *FakeObjectStoreUsers) List(opts v1.ListOptions) (result *v1beta1.ObjectStoreUserList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(objectstoreusersResource, objectstoreusersKind, c.ns, opts), &v1beta1.ObjectStoreUserList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1beta1.ObjectStoreUserList{}
|
||||
for _, item := range obj.(*v1beta1.ObjectStoreUserList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested objectStoreUsers.
|
||||
func (c *FakeObjectStoreUsers) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(objectstoreusersResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a objectStoreUser and creates it. Returns the server's representation of the objectStoreUser, and an error, if there is any.
|
||||
func (c *FakeObjectStoreUsers) Create(objectStoreUser *v1beta1.ObjectStoreUser) (result *v1beta1.ObjectStoreUser, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(objectstoreusersResource, c.ns, objectStoreUser), &v1beta1.ObjectStoreUser{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ObjectStoreUser), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a objectStoreUser and updates it. Returns the server's representation of the objectStoreUser, and an error, if there is any.
|
||||
func (c *FakeObjectStoreUsers) Update(objectStoreUser *v1beta1.ObjectStoreUser) (result *v1beta1.ObjectStoreUser, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(objectstoreusersResource, c.ns, objectStoreUser), &v1beta1.ObjectStoreUser{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ObjectStoreUser), err
|
||||
}
|
||||
|
||||
// Delete takes name of the objectStoreUser and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeObjectStoreUsers) Delete(name string, options *v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(objectstoreusersResource, c.ns, name), &v1beta1.ObjectStoreUser{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeObjectStoreUsers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(objectstoreusersResource, c.ns, listOptions)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1beta1.ObjectStoreUserList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched objectStoreUser.
|
||||
func (c *FakeObjectStoreUsers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.ObjectStoreUser, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(objectstoreusersResource, c.ns, name, data, subresources...), &v1beta1.ObjectStoreUser{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta1.ObjectStoreUser), err
|
||||
}
|
||||
@@ -24,4 +24,6 @@ type FilesystemExpansion interface{}
|
||||
|
||||
type ObjectStoreExpansion interface{}
|
||||
|
||||
type ObjectStoreUserExpansion interface{}
|
||||
|
||||
type PoolExpansion interface{}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
v1beta1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1beta1"
|
||||
scheme "github.com/rook/rook/pkg/client/clientset/versioned/scheme"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
rest "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// ObjectStoreUsersGetter has a method to return a ObjectStoreUserInterface.
|
||||
// A group's client should implement this interface.
|
||||
type ObjectStoreUsersGetter interface {
|
||||
ObjectStoreUsers(namespace string) ObjectStoreUserInterface
|
||||
}
|
||||
|
||||
// ObjectStoreUserInterface has methods to work with ObjectStoreUser resources.
|
||||
type ObjectStoreUserInterface interface {
|
||||
Create(*v1beta1.ObjectStoreUser) (*v1beta1.ObjectStoreUser, error)
|
||||
Update(*v1beta1.ObjectStoreUser) (*v1beta1.ObjectStoreUser, error)
|
||||
Delete(name string, options *v1.DeleteOptions) error
|
||||
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
|
||||
Get(name string, options v1.GetOptions) (*v1beta1.ObjectStoreUser, error)
|
||||
List(opts v1.ListOptions) (*v1beta1.ObjectStoreUserList, error)
|
||||
Watch(opts v1.ListOptions) (watch.Interface, error)
|
||||
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.ObjectStoreUser, err error)
|
||||
ObjectStoreUserExpansion
|
||||
}
|
||||
|
||||
// objectStoreUsers implements ObjectStoreUserInterface
|
||||
type objectStoreUsers struct {
|
||||
client rest.Interface
|
||||
ns string
|
||||
}
|
||||
|
||||
// newObjectStoreUsers returns a ObjectStoreUsers
|
||||
func newObjectStoreUsers(c *CephV1beta1Client, namespace string) *objectStoreUsers {
|
||||
return &objectStoreUsers{
|
||||
client: c.RESTClient(),
|
||||
ns: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// Get takes name of the objectStoreUser, and returns the corresponding objectStoreUser object, and an error if there is any.
|
||||
func (c *objectStoreUsers) Get(name string, options v1.GetOptions) (result *v1beta1.ObjectStoreUser, err error) {
|
||||
result = &v1beta1.ObjectStoreUser{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("objectstoreusers").
|
||||
Name(name).
|
||||
VersionedParams(&options, scheme.ParameterCodec).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ObjectStoreUsers that match those selectors.
|
||||
func (c *objectStoreUsers) List(opts v1.ListOptions) (result *v1beta1.ObjectStoreUserList, err error) {
|
||||
result = &v1beta1.ObjectStoreUserList{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("objectstoreusers").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested objectStoreUsers.
|
||||
func (c *objectStoreUsers) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||
opts.Watch = true
|
||||
return c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("objectstoreusers").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Watch()
|
||||
}
|
||||
|
||||
// Create takes the representation of a objectStoreUser and creates it. Returns the server's representation of the objectStoreUser, and an error, if there is any.
|
||||
func (c *objectStoreUsers) Create(objectStoreUser *v1beta1.ObjectStoreUser) (result *v1beta1.ObjectStoreUser, err error) {
|
||||
result = &v1beta1.ObjectStoreUser{}
|
||||
err = c.client.Post().
|
||||
Namespace(c.ns).
|
||||
Resource("objectstoreusers").
|
||||
Body(objectStoreUser).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a objectStoreUser and updates it. Returns the server's representation of the objectStoreUser, and an error, if there is any.
|
||||
func (c *objectStoreUsers) Update(objectStoreUser *v1beta1.ObjectStoreUser) (result *v1beta1.ObjectStoreUser, err error) {
|
||||
result = &v1beta1.ObjectStoreUser{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("objectstoreusers").
|
||||
Name(objectStoreUser.Name).
|
||||
Body(objectStoreUser).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes name of the objectStoreUser and deletes it. Returns an error if one occurs.
|
||||
func (c *objectStoreUsers) Delete(name string, options *v1.DeleteOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("objectstoreusers").
|
||||
Name(name).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *objectStoreUsers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("objectstoreusers").
|
||||
VersionedParams(&listOptions, scheme.ParameterCodec).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched objectStoreUser.
|
||||
func (c *objectStoreUsers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.ObjectStoreUser, err error) {
|
||||
result = &v1beta1.ObjectStoreUser{}
|
||||
err = c.client.Patch(pt).
|
||||
Namespace(c.ns).
|
||||
Resource("objectstoreusers").
|
||||
SubResource(subresources...).
|
||||
Name(name).
|
||||
Body(data).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/rook/rook/pkg/operator/ceph/cluster/osd"
|
||||
"github.com/rook/rook/pkg/operator/ceph/file"
|
||||
"github.com/rook/rook/pkg/operator/ceph/object"
|
||||
"github.com/rook/rook/pkg/operator/ceph/object/user"
|
||||
"github.com/rook/rook/pkg/operator/ceph/pool"
|
||||
"github.com/rook/rook/pkg/operator/discover"
|
||||
"github.com/rook/rook/pkg/operator/k8sutil"
|
||||
@@ -233,6 +234,10 @@ func (c *ClusterController) onAdd(obj interface{}) {
|
||||
objectStoreController := object.NewObjectStoreController(c.context, c.rookImage, cluster.Spec.CephVersion, cluster.Spec.Network.HostNetwork, cluster.ownerRef)
|
||||
objectStoreController.StartWatch(cluster.Namespace, cluster.stopCh)
|
||||
|
||||
// Start object store user CRD watcher
|
||||
objectStoreUserController := objectuser.NewObjectStoreUserController(c.context, cluster.ownerRef)
|
||||
objectStoreUserController.StartWatch(cluster.Namespace, cluster.stopCh)
|
||||
|
||||
// Start file system CRD watcher
|
||||
fileController := file.NewFilesystemController(c.context, c.rookImage, cluster.Spec.CephVersion, cluster.Spec.Network.HostNetwork, cluster.ownerRef)
|
||||
fileController.StartWatch(cluster.Namespace, cluster.stopCh)
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
Copyright 2018 The Rook Authors. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package objectuser to manage a rook object store user.
|
||||
package objectuser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
opkit "github.com/rook/operator-kit"
|
||||
cephv1beta1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1beta1"
|
||||
"github.com/rook/rook/pkg/clusterd"
|
||||
cephrgw "github.com/rook/rook/pkg/daemon/ceph/rgw"
|
||||
"github.com/rook/rook/pkg/operator/k8sutil"
|
||||
"k8s.io/api/core/v1"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
customResourceName = "objectstoreuser"
|
||||
customResourceNamePlural = "objectstoreusers"
|
||||
AppName = "rook-ceph-rgw"
|
||||
)
|
||||
|
||||
var logger = capnslog.NewPackageLogger("github.com/rook/rook", "op-object")
|
||||
|
||||
// ObjectStoreResource represents the object store user custom resource
|
||||
var ObjectStoreUserResource = opkit.CustomResource{
|
||||
Name: customResourceName,
|
||||
Plural: customResourceNamePlural,
|
||||
Group: cephv1beta1.CustomResourceGroup,
|
||||
Version: cephv1beta1.Version,
|
||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||
Kind: reflect.TypeOf(cephv1beta1.ObjectStoreUser{}).Name(),
|
||||
}
|
||||
|
||||
// ObjectStoreUserController represents a controller object for object store user custom resources
|
||||
type ObjectStoreUserController struct {
|
||||
context *clusterd.Context
|
||||
ownerRef metav1.OwnerReference
|
||||
}
|
||||
|
||||
// NewObjectStoreUserController create controller for watching object store user custom resources created
|
||||
func NewObjectStoreUserController(context *clusterd.Context, ownerRef metav1.OwnerReference) *ObjectStoreUserController {
|
||||
return &ObjectStoreUserController{
|
||||
context: context,
|
||||
ownerRef: ownerRef,
|
||||
}
|
||||
}
|
||||
|
||||
// StartWatch watches for instances of ObjectStoreUser custom resources and acts on them
|
||||
func (c *ObjectStoreUserController) StartWatch(namespace string, stopCh chan struct{}) error {
|
||||
|
||||
resourceHandlerFuncs := cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: c.onAdd,
|
||||
UpdateFunc: c.onUpdate,
|
||||
DeleteFunc: c.onDelete,
|
||||
}
|
||||
|
||||
logger.Infof("start watching object store user resources in namespace %s", namespace)
|
||||
watcher := opkit.NewWatcher(ObjectStoreUserResource, namespace, resourceHandlerFuncs, c.context.RookClientset.CephV1beta1().RESTClient())
|
||||
go watcher.Watch(&cephv1beta1.ObjectStoreUser{}, stopCh)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ObjectStoreUserController) onAdd(obj interface{}) {
|
||||
user, err := getObjectStoreUserObject(obj)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get objectstoreuser object: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = c.createUser(c.context, user); err != nil {
|
||||
logger.Errorf("failed to create object store user %s. %+v", user.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ObjectStoreUserController) onUpdate(oldObj, newObj interface{}) {
|
||||
// TODO: Add update code here after features are added which require updates.
|
||||
}
|
||||
|
||||
func (c *ObjectStoreUserController) onDelete(obj interface{}) {
|
||||
user, err := getObjectStoreUserObject(obj)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get objectstoreuser object: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = deleteUser(c.context, user); err != nil {
|
||||
logger.Errorf("failed to delete object store user %s. %+v", user.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ObjectStoreUserController) storeUserOwners(store *cephv1beta1.ObjectStoreUser) []metav1.OwnerReference {
|
||||
// Only set the cluster crd as the owner of the object store user resources.
|
||||
// If the object store user crd is deleted, the operator will explicitly remove the object store user resources.
|
||||
// If the object store user crd still exists when the cluster crd is deleted, this will make sure the object store user
|
||||
// resources are cleaned up.
|
||||
return []metav1.OwnerReference{c.ownerRef}
|
||||
}
|
||||
|
||||
func getObjectStoreUserObject(obj interface{}) (objectstoreuser *cephv1beta1.ObjectStoreUser, err error) {
|
||||
var ok bool
|
||||
objectstoreuser, ok = obj.(*cephv1beta1.ObjectStoreUser)
|
||||
if ok {
|
||||
// the objectstoreuser object is of the latest type, simply return it
|
||||
return objectstoreuser.DeepCopy(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("not a known objectstoreuser object: %+v", obj)
|
||||
}
|
||||
|
||||
// Create the user
|
||||
func (c *ObjectStoreUserController) createUser(context *clusterd.Context, u *cephv1beta1.ObjectStoreUser) error {
|
||||
// validate the user settings
|
||||
if err := ValidateUser(context, u); err != nil {
|
||||
return fmt.Errorf("invalid user %s arguments. %+v", u.Name, err)
|
||||
}
|
||||
//Set DisplayName to match Name if DisplayName is not set
|
||||
displayName := u.Spec.DisplayName
|
||||
if len(displayName) == 0 {
|
||||
displayName = u.Name
|
||||
}
|
||||
|
||||
// create the user
|
||||
logger.Infof("creating user %s in namespace %s", u.Name, u.Namespace)
|
||||
userConfig := cephrgw.ObjectUser{
|
||||
UserID: u.Name,
|
||||
DisplayName: &displayName,
|
||||
}
|
||||
objContext := cephrgw.NewContext(context, u.Spec.Store, u.Namespace)
|
||||
|
||||
user, rgwerr, err := cephrgw.CreateUser(objContext, userConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create user %s. RadosGW returned error %d: %+v", u.Name, rgwerr, err)
|
||||
}
|
||||
|
||||
// Store the keys in a secret
|
||||
secrets := map[string]string{
|
||||
"AccessKey": *user.AccessKey,
|
||||
"SecretKey": *user.SecretKey,
|
||||
}
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("rook-ceph-object-user-%s-%s", u.Spec.Store, u.Name),
|
||||
Namespace: u.Namespace,
|
||||
Labels: map[string]string{
|
||||
"app": AppName,
|
||||
"user": u.Name,
|
||||
"rook_cluster": u.Namespace,
|
||||
"rook_object_store": u.Spec.Store,
|
||||
},
|
||||
},
|
||||
StringData: secrets,
|
||||
Type: k8sutil.RookType,
|
||||
}
|
||||
k8sutil.SetOwnerRef(context.Clientset, u.Namespace, &secret.ObjectMeta, &c.ownerRef)
|
||||
|
||||
_, err = context.Clientset.CoreV1().Secrets(u.Namespace).Create(secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save user %s secret. %+v", u.Name, err)
|
||||
}
|
||||
logger.Infof("created user %s", u.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete the user
|
||||
func deleteUser(context *clusterd.Context, u *cephv1beta1.ObjectStoreUser) error {
|
||||
objContext := cephrgw.NewContext(context, u.Spec.Store, u.Namespace)
|
||||
_, rgwerr, err := cephrgw.DeleteUser(objContext, u.Name)
|
||||
if err != nil {
|
||||
if rgwerr == 3 {
|
||||
logger.Infof("user %s does not exist in store %s", u.Name, u.Spec.Store)
|
||||
} else {
|
||||
return fmt.Errorf("failed to delete user '%s': %+v", u.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = context.Clientset.CoreV1().Secrets(u.Namespace).Delete("rook-ceph-object-user-"+u.Name, &metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
logger.Warningf("failed to delete user %s secret. %+v", u.Name, err)
|
||||
}
|
||||
|
||||
logger.Infof("user %s deleted successfully", u.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate the user arguments
|
||||
func ValidateUser(context *clusterd.Context, u *cephv1beta1.ObjectStoreUser) error {
|
||||
if u.Name == "" {
|
||||
return fmt.Errorf("missing name")
|
||||
}
|
||||
if u.Namespace == "" {
|
||||
return fmt.Errorf("missing namespace")
|
||||
}
|
||||
if u.Spec.Store == "" {
|
||||
return fmt.Errorf("missing store")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2018 The Rook Authors. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package objectuser to manage a rook object store.
|
||||
package objectuser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
cephv1beta1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1beta1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetObjectStoreUserObject(t *testing.T) {
|
||||
// get a current version objectstoreuser object, should return with no error
|
||||
objectuser, err := getObjectStoreUserObject(&cephv1beta1.ObjectStoreUser{})
|
||||
assert.NotNil(t, objectuser)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// try to get an object that isn't a objectstoreuser, should return with an error
|
||||
objectuser, err = getObjectStoreUserObject(&map[string]string{})
|
||||
assert.Nil(t, objectuser)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/rook/rook/pkg/operator/ceph/cluster"
|
||||
"github.com/rook/rook/pkg/operator/ceph/file"
|
||||
"github.com/rook/rook/pkg/operator/ceph/object"
|
||||
"github.com/rook/rook/pkg/operator/ceph/object/user"
|
||||
"github.com/rook/rook/pkg/operator/ceph/pool"
|
||||
"github.com/rook/rook/pkg/operator/ceph/provisioner"
|
||||
"github.com/rook/rook/pkg/operator/ceph/provisioner/controller"
|
||||
@@ -74,7 +75,7 @@ type Operator struct {
|
||||
func New(context *clusterd.Context, volumeAttachmentWrapper attachment.Attachment, rookImage, securityAccount string) *Operator {
|
||||
clusterController := cluster.NewClusterController(context, rookImage, volumeAttachmentWrapper)
|
||||
|
||||
schemes := []opkit.CustomResource{cluster.ClusterResource, pool.PoolResource, object.ObjectStoreResource,
|
||||
schemes := []opkit.CustomResource{cluster.ClusterResource, pool.PoolResource, object.ObjectStoreResource, objectuser.ObjectStoreUserResource,
|
||||
file.FilesystemResource, attachment.VolumeResource}
|
||||
return &Operator{
|
||||
context: context,
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/rook/rook/pkg/operator/ceph/cluster"
|
||||
"github.com/rook/rook/pkg/operator/ceph/file"
|
||||
"github.com/rook/rook/pkg/operator/ceph/object"
|
||||
"github.com/rook/rook/pkg/operator/ceph/object/user"
|
||||
"github.com/rook/rook/pkg/operator/ceph/pool"
|
||||
"github.com/rook/rook/pkg/operator/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -39,10 +40,10 @@ func TestOperator(t *testing.T) {
|
||||
assert.NotNil(t, o.clusterController)
|
||||
assert.NotNil(t, o.resources)
|
||||
assert.Equal(t, context, o.context)
|
||||
assert.Equal(t, len(o.resources), 5)
|
||||
assert.Equal(t, len(o.resources), 6)
|
||||
for _, r := range o.resources {
|
||||
if r.Name != cluster.ClusterResource.Name && r.Name != pool.PoolResource.Name && r.Name != object.ObjectStoreResource.Name &&
|
||||
r.Name != file.FilesystemResource.Name && r.Name != attachment.VolumeResource.Name {
|
||||
r.Name != file.FilesystemResource.Name && r.Name != attachment.VolumeResource.Name && r.Name != objectuser.ObjectStoreUserResource.Name {
|
||||
assert.Fail(t, fmt.Sprintf("Resource %s is not valid", r.Name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2018 The Rook Authors. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package clients
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/rook/rook/pkg/daemon/ceph/rgw"
|
||||
"github.com/rook/rook/tests/framework/installer"
|
||||
"github.com/rook/rook/tests/framework/utils"
|
||||
)
|
||||
|
||||
// ObjectUserOperation is wrapper for k8s rook object user operations
|
||||
type ObjectUserOperation struct {
|
||||
k8sh *utils.K8sHelper
|
||||
manifests installer.CephManifests
|
||||
}
|
||||
|
||||
// CreateObjectUserOperation creates new rook object user client
|
||||
func CreateObjectUserOperation(k8sh *utils.K8sHelper, manifests installer.CephManifests) *ObjectUserOperation {
|
||||
return &ObjectUserOperation{k8sh, manifests}
|
||||
}
|
||||
|
||||
// ObjectUserGet Function to get the details of an object user from radosgw
|
||||
func (o *ObjectUserOperation) GetUser(namespace string, store string, userid string) (*rgw.ObjectUser, error) {
|
||||
context := o.k8sh.MakeContext()
|
||||
rgwcontext := rgw.NewContext(context, store, namespace)
|
||||
userinfo, _, err := rgw.GetUser(rgwcontext, userid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get user info: %+v", err)
|
||||
}
|
||||
return userinfo, nil
|
||||
}
|
||||
|
||||
// UserSecretExists Function to check that user secret was created
|
||||
func (o *ObjectUserOperation) UserSecretExists(namespace string, store string, userid string) bool {
|
||||
_, err := o.k8sh.GetResource("-n", namespace, "secrets", "-l", "rook_object_store="+store, "-l", "user="+userid)
|
||||
if err == nil {
|
||||
logger.Infof("Object User Secret Exists")
|
||||
return true
|
||||
}
|
||||
logger.Infof("Unable to find user secret")
|
||||
return false
|
||||
}
|
||||
|
||||
// ObjectUserCreate Function to create a object store user in rook
|
||||
func (o *ObjectUserOperation) Create(namespace string, userid string, displayName string, store string) error {
|
||||
|
||||
logger.Infof("creating the object store user via CRD")
|
||||
if _, err := o.k8sh.ResourceOperation("create", o.manifests.GetObjectStoreUser(namespace, userid, displayName, store)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ObjectUserOperation) Delete(namespace string, userid string) error {
|
||||
|
||||
logger.Infof("Deleting the object store user via CRD")
|
||||
if _, err := o.k8sh.DeleteResource("-n", namespace, "ObjectStoreUser", userid); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -30,11 +30,12 @@ var (
|
||||
|
||||
//TestClient is a wrapper for test client, containing interfaces for all rook operations
|
||||
type TestClient struct {
|
||||
BlockClient *BlockOperation
|
||||
FSClient *FilesystemOperation
|
||||
ObjectClient *ObjectOperation
|
||||
PoolClient *PoolOperation
|
||||
k8sh *utils.K8sHelper
|
||||
BlockClient *BlockOperation
|
||||
FSClient *FilesystemOperation
|
||||
ObjectClient *ObjectOperation
|
||||
ObjectUserClient *ObjectUserOperation
|
||||
PoolClient *PoolOperation
|
||||
k8sh *utils.K8sHelper
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -48,6 +49,7 @@ func CreateTestClient(k8sHelper *utils.K8sHelper, manifests installer.CephManife
|
||||
CreateBlockOperation(k8sHelper, manifests),
|
||||
CreateFilesystemOperation(k8sHelper, manifests),
|
||||
CreateObjectOperation(k8sHelper, manifests),
|
||||
CreateObjectUserOperation(k8sHelper, manifests),
|
||||
CreatePoolOperation(k8sHelper, manifests),
|
||||
k8sHelper,
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ func (h *CephInstaller) UninstallRookFromMultipleNS(helmInstalled bool, systemNa
|
||||
}
|
||||
|
||||
logger.Infof("removing the operator from namespace %s", systemNamespace)
|
||||
_, err = h.k8shelper.DeleteResource("crd", "clusters.ceph.rook.io", "pools.ceph.rook.io", "objectstores.ceph.rook.io", "filesystems.ceph.rook.io", "volumes.rook.io")
|
||||
_, err = h.k8shelper.DeleteResource("crd", "clusters.ceph.rook.io", "pools.ceph.rook.io", "objectstores.ceph.rook.io", "objectstoreusers.ceph.rook.io", "filesystems.ceph.rook.io", "volumes.rook.io")
|
||||
checkError(h.T(), err, "cannot delete CRDs")
|
||||
|
||||
if helmInstalled {
|
||||
|
||||
@@ -38,6 +38,7 @@ type CephManifests interface {
|
||||
GetBlockPoolStorageClass(namespace string, poolName string, storageClassName string, reclaimPolicy string) string
|
||||
GetFilesystem(namepace, name string, activeCount int) string
|
||||
GetObjectStore(namespace, name string, replicaCount, port int) string
|
||||
GetObjectStoreUser(namespace, name string, displayName string, store string) string
|
||||
}
|
||||
|
||||
type ClusterSettings struct {
|
||||
@@ -110,6 +111,22 @@ spec:
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: objectstoreusers.ceph.rook.io
|
||||
spec:
|
||||
group: ceph.rook.io
|
||||
names:
|
||||
kind: ObjectStoreUser
|
||||
listKind: ObjectStoreUserList
|
||||
plural: objectstoreusers
|
||||
singular: objectstoreuser
|
||||
shortNames:
|
||||
- rcou
|
||||
scope: Namespaced
|
||||
version: v1beta1
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: pools.ceph.rook.io
|
||||
spec:
|
||||
@@ -619,3 +636,14 @@ spec:
|
||||
allNodes: false
|
||||
`
|
||||
}
|
||||
|
||||
func (m *CephManifestsMaster) GetObjectStoreUser(namespace, name string, displayName string, store string) string {
|
||||
return `apiVersion: ceph.rook.io/v1beta1
|
||||
kind: ObjectStoreUser
|
||||
metadata:
|
||||
name: ` + name + `
|
||||
namespace: ` + namespace + `
|
||||
spec:
|
||||
displayName: ` + displayName + `
|
||||
store: ` + store
|
||||
}
|
||||
|
||||
@@ -574,3 +574,7 @@ spec:
|
||||
allNodes: false
|
||||
`
|
||||
}
|
||||
|
||||
func (m *CephManifestsV0_8) GetObjectStoreUser(namespace, name string, displayName string, store string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -51,20 +52,20 @@ func runObjectE2ETest(helper *clients.TestClient, k8sh *utils.K8sHelper, s suite
|
||||
require.Nil(s.T(), cobsErr)
|
||||
logger.Infof("Object store created successfully")
|
||||
|
||||
/* TODO: Reenable this test after we have the object user CRD
|
||||
|
||||
logger.Infof("Step 1 : Create Object Store User")
|
||||
initialUsers, _ := helper.ObjectClient.ObjectListUser(storeName)
|
||||
_, cosuErr := helper.ObjectClient.CreateUser(storeName, userid, userdisplayname)
|
||||
cosuErr := helper.ObjectUserClient.Create(namespace, userid, userdisplayname, storeName)
|
||||
require.Nil(s.T(), cosuErr)
|
||||
usersAfterCreate, _ := helper.ObjectClient.ObjectListUser(storeName)
|
||||
require.Equal(s.T(), len(initialUsers)+1, len(usersAfterCreate), "Make sure user list count is increased by 1")
|
||||
getuserData, guErr := helper.ObjectClient.ObjectGetUser(storeName, userid)
|
||||
require.Nil(s.T(), guErr)
|
||||
require.Equal(s.T(), userid, getuserData.UserID, "Check user id returned")
|
||||
require.Equal(s.T(), userdisplayname, *getuserData.DisplayName, "Check user name returned")
|
||||
logger.Infof("Waiting 5 seconds to ensure user was created")
|
||||
time.Sleep(5 * time.Second)
|
||||
require.True(s.T(), helper.ObjectUserClient.UserSecretExists(namespace, userid, storeName), "make sure user secret was created")
|
||||
userInfo, gosuErr := helper.ObjectUserClient.GetUser(namespace, storeName, userid)
|
||||
require.Nil(s.T(), gosuErr)
|
||||
require.Equal(s.T(), userid, userInfo.UserID)
|
||||
require.Equal(s.T(), userdisplayname, *userInfo.DisplayName)
|
||||
logger.Infof("Object store user created successfully")
|
||||
|
||||
/* TODO: We need bucket management tests.
|
||||
|
||||
logger.Infof("Step 2 : Get connection information")
|
||||
conninfo, conninfoError := helper.ObjectClient.ObjectGetUser(storeName, userid)
|
||||
require.Nil(s.T(), conninfoError)
|
||||
@@ -115,13 +116,12 @@ func runObjectE2ETest(helper *clients.TestClient, k8sh *utils.K8sHelper, s suite
|
||||
require.Equal(s.T(), len(initialBuckets), len(BucketsAfterDelete), "Make sure new bucket is deleted")
|
||||
logger.Infof("Bucket deleted successfully")
|
||||
|
||||
logger.Infof("Step 8 : Delete User")
|
||||
usersBeforeDelete, _ := helper.ObjectClient.ObjectListUser(storeName)
|
||||
helper.ObjectClient.DeleteUser(storeName, userid)
|
||||
usersAfterDelete, _ := helper.ObjectClient.ObjectListUser(storeName)
|
||||
require.Equal(s.T(), len(usersBeforeDelete)-1, len(usersAfterDelete), "Make sure user list count is reduced by 1")
|
||||
*/ // End of object operation tests
|
||||
|
||||
logger.Infof("Step 2 : Test Deleting User")
|
||||
dosuErr := helper.ObjectUserClient.Delete(namespace, userid)
|
||||
require.Nil(s.T(), dosuErr)
|
||||
logger.Infof("Object store user deleted successfully")
|
||||
*/
|
||||
|
||||
logger.Infof("Check that MGRs are not in a crashloop")
|
||||
assert.True(s.T(), k8sh.CheckPodCountAndState("rook-ceph-mgr", namespace, 1, "Running"))
|
||||
|
||||
Reference in New Issue
Block a user