* init commit for workspace item

* add support for workspace item
This commit is contained in:
Xinyun Zhou 2019-08-09 09:37:23 +10:00 коммит произвёл azadeh khojandi
Родитель 0d2b10e078
Коммит 0f722a710f
17 изменённых файлов: 838 добавлений и 9 удалений

Просмотреть файл

@ -17,3 +17,6 @@ resources:
- group: databricks
version: v1
kind: DbfsBlock
- group: databricks
version: v1
kind: WorkspaceItem

Просмотреть файл

@ -0,0 +1,115 @@
/*
Copyright 2019 microsoft.
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 v1
import (
"crypto/sha1"
"encoding/base64"
"fmt"
dbmodels "github.com/xinsnake/databricks-sdk-golang/azure/models"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// WorkspaceItemSpec defines the desired state of WorkspaceItem
type WorkspaceItemSpec struct {
Content string `json:"content,omitempty"`
Path string `json:"path,omitempty"`
Language dbmodels.Language `json:"language,omitempty"`
Format dbmodels.ExportFormat `json:"format,omitempty"`
}
// WorkspaceItemStatus defines the observed state of WorkspaceItem
type WorkspaceItemStatus struct {
ObjectInfo *dbmodels.ObjectInfo `json:"object_info,omitempty"`
ObjectHash string `json:"object_hash,omitempty"`
}
// +kubebuilder:object:root=true
// WorkspaceItem is the Schema for the workspaceitems API
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",priority=0
// +kubebuilder:printcolumn:name="SHA1SUM",type="string",JSONPath=".status.object_hash",priority=0
// +kubebuilder:printcolumn:name="Language",type="string",JSONPath=".status.object_info.language",priority=0
// +kubebuilder:printcolumn:name="Type",type="string",JSONPath=".status.object_info.object_type",priority=1
// +kubebuilder:printcolumn:name="Path",type="string",JSONPath=".status.object_info.path",priority=1
type WorkspaceItem struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec *WorkspaceItemSpec `json:"spec,omitempty"`
Status *WorkspaceItemStatus `json:"status,omitempty"`
}
func (wi *WorkspaceItem) IsBeingDeleted() bool {
return !wi.ObjectMeta.DeletionTimestamp.IsZero()
}
func (wi *WorkspaceItem) IsSubmitted() bool {
if wi.Status == nil || wi.Status.ObjectInfo == nil || wi.Status.ObjectInfo.Path == "" {
return false
}
return true
}
// IsUpToDate tells you whether the data is up-to-date with the status
func (wi *WorkspaceItem) IsUpToDate() bool {
if wi.Status == nil {
return false
}
h := wi.GetHash()
return h == wi.Status.ObjectHash
}
// GetHash returns the sha1 hash of the decoded data attribute
func (wi *WorkspaceItem) GetHash() string {
data, err := base64.StdEncoding.DecodeString(wi.Spec.Content)
if err != nil {
return ""
}
h := sha1.New()
h.Write(data)
bs := h.Sum(nil)
return fmt.Sprintf("%x", bs)
}
const WorkspaceItemFinalizerName = "workspaceitem.finalizers.databricks.microsoft.com"
func (wi *WorkspaceItem) HasFinalizer(finalizerName string) bool {
return containsString(wi.ObjectMeta.Finalizers, finalizerName)
}
func (wi *WorkspaceItem) AddFinalizer(finalizerName string) {
wi.ObjectMeta.Finalizers = append(wi.ObjectMeta.Finalizers, finalizerName)
}
func (wi *WorkspaceItem) RemoveFinalizer(finalizerName string) {
wi.ObjectMeta.Finalizers = removeString(wi.ObjectMeta.Finalizers, finalizerName)
}
// +kubebuilder:object:root=true
// WorkspaceItemList contains a list of WorkspaceItem
type WorkspaceItemList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []WorkspaceItem `json:"items"`
}
func init() {
SchemeBuilder.Register(&WorkspaceItem{}, &WorkspaceItemList{})
}

Просмотреть файл

@ -0,0 +1,149 @@
/*
Copyright 2019 microsoft.
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 v1
import (
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
dbmodels "github.com/xinsnake/databricks-sdk-golang/azure/models"
"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
// These tests are written in BDD-style using Ginkgo framework. Refer to
// http://onsi.github.io/ginkgo to learn more.
var _ = Describe("WorkspaceItem", func() {
var (
key types.NamespacedName
created, fetched *WorkspaceItem
)
BeforeEach(func() {
// Add any setup steps that needs to be executed before each test
})
AfterEach(func() {
// Add any teardown steps that needs to be executed after each test
})
// Add Tests for OpenAPI validation (or additonal CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.
Context("Create API", func() {
It("should create an object successfully", func() {
key = types.NamespacedName{
Name: "foo",
Namespace: "default",
}
created = &WorkspaceItem{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
}}
By("creating an API obj")
Expect(k8sClient.Create(context.TODO(), created)).To(Succeed())
fetched = &WorkspaceItem{}
Expect(k8sClient.Get(context.TODO(), key, fetched)).To(Succeed())
Expect(fetched).To(Equal(created))
By("deleting the created object")
Expect(k8sClient.Delete(context.TODO(), created)).To(Succeed())
Expect(k8sClient.Get(context.TODO(), key, created)).ToNot(Succeed())
})
It("should correctly handle isSubmitted", func() {
wiItem := &WorkspaceItem{
Status: &WorkspaceItemStatus{
ObjectInfo: &dbmodels.ObjectInfo{
Path: "",
},
},
}
Expect(wiItem.IsSubmitted()).To(BeFalse())
wiItem2 := &WorkspaceItem{
Status: &WorkspaceItemStatus{
ObjectInfo: &dbmodels.ObjectInfo{
Path: "/test-path",
},
},
}
Expect(wiItem2.IsSubmitted()).To(BeTrue())
wiItem3 := &WorkspaceItem{
Status: &WorkspaceItemStatus{
ObjectInfo: nil,
},
}
Expect(wiItem3.IsSubmitted()).To(BeFalse())
})
It("should correctly handle finalizers", func() {
wiItem := &WorkspaceItem{
ObjectMeta: metav1.ObjectMeta{
DeletionTimestamp: &metav1.Time{
Time: time.Now(),
},
},
}
Expect(wiItem.IsBeingDeleted()).To(BeTrue())
wiItem.AddFinalizer(WorkspaceItemFinalizerName)
Expect(len(wiItem.GetFinalizers())).To(Equal(1))
Expect(wiItem.HasFinalizer(WorkspaceItemFinalizerName)).To(BeTrue())
wiItem.RemoveFinalizer(WorkspaceItemFinalizerName)
Expect(len(wiItem.GetFinalizers())).To(Equal(0))
Expect(wiItem.HasFinalizer(WorkspaceItemFinalizerName)).To(BeFalse())
})
It("should correctly handle file hash", func() {
wiItem := &WorkspaceItem{
Spec: &WorkspaceItemSpec{
Content: "dGVzdA==",
},
}
Expect(wiItem.GetHash()).To(Equal("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"))
Expect(wiItem.IsUpToDate()).To(BeFalse())
wiItem.Status = &WorkspaceItemStatus{
ObjectHash: "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
}
Expect(wiItem.IsUpToDate()).To(BeTrue())
wiItemError := &WorkspaceItem{
Spec: &WorkspaceItemSpec{
Content: "invalid_base64",
},
}
Expect(wiItemError.GetHash()).To(Equal(""))
})
})
})

Просмотреть файл

@ -651,3 +651,105 @@ func (in *SecretScopeValueFrom) DeepCopy() *SecretScopeValueFrom {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceItem) DeepCopyInto(out *WorkspaceItem) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.Spec != nil {
in, out := &in.Spec, &out.Spec
*out = new(WorkspaceItemSpec)
**out = **in
}
if in.Status != nil {
in, out := &in.Status, &out.Status
*out = new(WorkspaceItemStatus)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceItem.
func (in *WorkspaceItem) DeepCopy() *WorkspaceItem {
if in == nil {
return nil
}
out := new(WorkspaceItem)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WorkspaceItem) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceItemList) DeepCopyInto(out *WorkspaceItemList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]WorkspaceItem, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceItemList.
func (in *WorkspaceItemList) DeepCopy() *WorkspaceItemList {
if in == nil {
return nil
}
out := new(WorkspaceItemList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WorkspaceItemList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceItemSpec) DeepCopyInto(out *WorkspaceItemSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceItemSpec.
func (in *WorkspaceItemSpec) DeepCopy() *WorkspaceItemSpec {
if in == nil {
return nil
}
out := new(WorkspaceItemSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceItemStatus) DeepCopyInto(out *WorkspaceItemStatus) {
*out = *in
if in.ObjectInfo != nil {
in, out := &in.ObjectInfo, &out.ObjectInfo
*out = new(models.ObjectInfo)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceItemStatus.
func (in *WorkspaceItemStatus) DeepCopy() *WorkspaceItemStatus {
if in == nil {
return nil
}
out := new(WorkspaceItemStatus)
in.DeepCopyInto(out)
return out
}

Просмотреть файл

@ -0,0 +1,86 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
name: workspaceitems.databricks.microsoft.com
spec:
additionalPrinterColumns:
- JSONPath: .metadata.creationTimestamp
name: Age
type: date
- JSONPath: .status.object_hash
name: SHA1SUM
type: string
- JSONPath: .status.object_info.language
name: Language
type: string
- JSONPath: .status.object_info.object_type
name: Type
priority: 1
type: string
- JSONPath: .status.object_info.path
name: Path
priority: 1
type: string
group: databricks.microsoft.com
names:
kind: WorkspaceItem
plural: workspaceitems
scope: ""
subresources: {}
validation:
openAPIV3Schema:
description: WorkspaceItem is the Schema for the workspaceitems API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: WorkspaceItemSpec defines the desired state of WorkspaceItem
properties:
content:
type: string
format:
type: string
language:
type: string
path:
type: string
type: object
status:
description: WorkspaceItemStatus defines the observed state of WorkspaceItem
properties:
object_hash:
type: string
object_info:
properties:
language:
type: string
object_type:
type: string
path:
type: string
type: object
type: object
type: object
versions:
- name: v1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

Просмотреть файл

@ -7,6 +7,7 @@ resources:
- bases/databricks.microsoft.com_runs.yaml
- bases/databricks.microsoft.com_dclusters.yaml
- bases/databricks.microsoft.com_dbfsblocks.yaml
- bases/databricks.microsoft.com_workspaceitems.yaml
# +kubebuilder:scaffold:crdkustomizeresource
patches:
@ -16,6 +17,7 @@ patches:
#- patches/webhook_in_runs.yaml
#- patches/webhook_in_dclusters.yaml
#- patches/webhook_in_dbfsblocks.yaml
#- patches/webhook_in_workspaceitems.yaml
# +kubebuilder:scaffold:crdkustomizewebhookpatch
# [CAINJECTION] patches here are for enabling the CA injection for each CRD
@ -24,6 +26,7 @@ patches:
#- patches/cainjection_in_runs.yaml
#- patches/cainjection_in_dclusters.yaml
#- patches/cainjection_in_dbfsblocks.yaml
#- patches/cainjection_in_workspaceitems.yaml
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.

Просмотреть файл

@ -0,0 +1,8 @@
# The following patch adds a directive for certmanager to inject CA into the CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
certmanager.k8s.io/inject-ca-from: $(NAMESPACE)/$(CERTIFICATENAME)
name: workspaceitems.databricks.microsoft.com

Просмотреть файл

@ -0,0 +1,17 @@
# The following patch enables conversion webhook for CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: workspaceitems.databricks.microsoft.com
spec:
conversion:
strategy: Webhook
webhookClientConfig:
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
caBundle: Cg==
service:
namespace: system
name: webhook-service
path: /convert

Просмотреть файл

@ -152,3 +152,23 @@ rules:
- get
- patch
- update
- apiGroups:
- databricks.microsoft.com
resources:
- workspaceitems
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- databricks.microsoft.com
resources:
- workspaceitems/status
verbs:
- get
- patch
- update

Просмотреть файл

@ -0,0 +1,11 @@
apiVersion: databricks.microsoft.com/v1
kind: WorkspaceItem
metadata:
name: workspaceitem-sample
spec:
content: |
MSsx
path: /ScalaExampleNotebook
language: SCALA
overwrite: true
format: SOURCE

Просмотреть файл

@ -80,12 +80,6 @@ var _ = BeforeSuite(func(done Done) {
err = databricksv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = databricksv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = databricksv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
@ -146,6 +140,14 @@ var _ = BeforeSuite(func(done Done) {
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
err = (&WorkspaceItemReconciler{
Client: k8sManager.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Run"),
Recorder: k8sManager.GetEventRecorderFor("workspaceitem-controller"),
APIClient: apiClient,
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
go func() {
err = k8sManager.Start(ctrl.SetupSignalHandler())
Expect(err).ToNot(HaveOccurred())

Просмотреть файл

@ -0,0 +1,95 @@
/*
Copyright 2019 microsoft.
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 controllers
import (
"context"
"fmt"
"github.com/go-logr/logr"
dbazure "github.com/xinsnake/databricks-sdk-golang/azure"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
databricksv1 "github.com/microsoft/azure-databricks-operator/api/v1"
)
// WorkspaceItemReconciler reconciles a WorkspaceItem object
type WorkspaceItemReconciler struct {
client.Client
Log logr.Logger
Recorder record.EventRecorder
APIClient dbazure.DBClient
}
// +kubebuilder:rbac:groups=databricks.microsoft.com,resources=workspaceitems,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=databricks.microsoft.com,resources=workspaceitems/status,verbs=get;update;patch
func (r *WorkspaceItemReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
_ = context.Background()
_ = r.Log.WithValues("workspaceitem", req.NamespacedName)
instance := &databricksv1.WorkspaceItem{}
r.Log.Info(fmt.Sprintf("Starting reconcile loop for %v", req.NamespacedName))
defer r.Log.Info(fmt.Sprintf("Finish reconcile loop for %v", req.NamespacedName))
if err := r.Get(context.Background(), req.NamespacedName, instance); err != nil {
if errors.IsNotFound(err) {
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
if instance.IsBeingDeleted() {
r.Log.Info(fmt.Sprintf("HandleFinalizer for %v", req.NamespacedName))
if err := r.handleFinalizer(instance); err != nil {
return ctrl.Result{}, fmt.Errorf("error when handling finalizer: %v", err)
}
r.Recorder.Event(instance, "Normal", "Deleted", "Object finalizer is deleted")
return ctrl.Result{}, nil
}
if !instance.HasFinalizer(databricksv1.WorkspaceItemFinalizerName) {
r.Log.Info(fmt.Sprintf("AddFinalizer for %v", req.NamespacedName))
if err := r.addFinalizer(instance); err != nil {
return ctrl.Result{}, fmt.Errorf("error when adding finalizer: %v", err)
}
r.Recorder.Event(instance, "Normal", "Added", "Object finalizer is added")
return ctrl.Result{}, nil
}
if !instance.IsSubmitted() || !instance.IsUpToDate() {
r.Log.Info(fmt.Sprintf("Submit for %v", req.NamespacedName))
if err := r.submit(instance); err != nil {
return ctrl.Result{}, fmt.Errorf("error when submitting workspace item: %v", err)
}
r.Recorder.Event(instance, "Normal", "Submitted", "Object is submitted")
return ctrl.Result{}, nil
}
return ctrl.Result{}, nil
}
func (r *WorkspaceItemReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&databricksv1.WorkspaceItem{}).
Complete(r)
}

Просмотреть файл

@ -0,0 +1,67 @@
/*
Copyright 2019 microsoft.
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 controllers
import (
"context"
"encoding/base64"
"fmt"
"time"
databricksv1 "github.com/microsoft/azure-databricks-operator/api/v1"
)
func (r *WorkspaceItemReconciler) submit(instance *databricksv1.WorkspaceItem) error {
r.Log.Info(fmt.Sprintf("Create item %s", instance.GetName()))
data, err := base64.StdEncoding.DecodeString(instance.Spec.Content)
if err != nil {
return err
}
err = r.APIClient.Workspace().Import(instance.Spec.Path, instance.Spec.Format, instance.Spec.Language, data, true)
if err != nil {
return err
}
time.Sleep(1 * time.Second)
// Refresh info
objectInfo, err := r.APIClient.Workspace().GetStatus(instance.Spec.Path)
if err != nil {
return err
}
instance.Status = &databricksv1.WorkspaceItemStatus{
ObjectInfo: &objectInfo,
ObjectHash: instance.GetHash(),
}
return r.Update(context.Background(), instance)
}
func (r *WorkspaceItemReconciler) delete(instance *databricksv1.WorkspaceItem) error {
r.Log.Info(fmt.Sprintf("Deleting item %s", instance.GetName()))
if instance.Status == nil || instance.Status.ObjectInfo == nil {
return nil
}
path := instance.Status.ObjectInfo.Path
return r.APIClient.Workspace().Delete(path, true)
}

Просмотреть файл

@ -0,0 +1,40 @@
/*
Copyright 2019 microsoft.
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 controllers
import (
"context"
databricksv1 "github.com/microsoft/azure-databricks-operator/api/v1"
)
func (r *WorkspaceItemReconciler) addFinalizer(instance *databricksv1.WorkspaceItem) error {
instance.AddFinalizer(databricksv1.WorkspaceItemFinalizerName)
return r.Update(context.Background(), instance)
}
func (r *WorkspaceItemReconciler) handleFinalizer(instance *databricksv1.WorkspaceItem) error {
if !instance.HasFinalizer(databricksv1.WorkspaceItemFinalizerName) {
return nil
}
if err := r.delete(instance); err != nil {
return err
}
instance.RemoveFinalizer(databricksv1.WorkspaceItemFinalizerName)
return r.Update(context.Background(), instance)
}

Просмотреть файл

@ -0,0 +1,100 @@
/*
Copyright 2019 microsoft.
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 controllers
import (
"context"
"time"
databricksv1 "github.com/microsoft/azure-databricks-operator/api/v1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
var _ = Describe("WorkspaceItem Controller", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
BeforeEach(func() {
// Add any setup steps that needs to be executed before each test
})
AfterEach(func() {
// Add any teardown steps that needs to be executed after each test
})
// Add Tests for OpenAPI validation (or additonal CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.
Context("Workspace Item", func() {
It("Should create successfully", func() {
key := types.NamespacedName{
Name: "block-greater-than-1mb",
Namespace: "default",
}
created := &databricksv1.WorkspaceItem{
ObjectMeta: metav1.ObjectMeta{
Name: key.Name,
Namespace: key.Namespace,
},
Spec: &databricksv1.WorkspaceItemSpec{
Content: "MSsx",
Path: "/test-notebook",
Language: "SCALA",
Format: "SOURCE",
},
}
// Create
Expect(k8sClient.Create(context.Background(), created)).Should(Succeed())
By("Expecting submitted")
Eventually(func() bool {
f := &databricksv1.WorkspaceItem{}
k8sClient.Get(context.Background(), key, f)
return f.IsSubmitted()
}, timeout, interval).Should(BeTrue())
// Update
updated := &databricksv1.WorkspaceItem{}
Expect(k8sClient.Get(context.Background(), key, updated)).Should(Succeed())
updated.Spec.Content = "MSsy"
Expect(k8sClient.Update(context.Background(), updated)).Should(Succeed())
// Delete
By("Expecting to delete successfully")
Eventually(func() error {
f := &databricksv1.WorkspaceItem{}
k8sClient.Get(context.Background(), key, f)
return k8sClient.Delete(context.Background(), f)
}, timeout, interval).Should(Succeed())
By("Expecting to delete finish")
Eventually(func() error {
f := &databricksv1.WorkspaceItem{}
return k8sClient.Get(context.Background(), key, f)
}, timeout, interval).ShouldNot(Succeed())
})
})
})

Просмотреть файл

@ -3,14 +3,15 @@
- Job (djob)
- Run (run)
- Secret Scope & Secret (secretscope)
- Cluster (dcluster)
- DBFS (dbfsblock)
- Workspace (workspaceitem)
# In Progress
- Cluster (dcluster)
- DBFS (dbfsblock)
- Libraries
# Future Development
- Libraries
- Group
- Token

10
main.go
Просмотреть файл

@ -128,6 +128,16 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "DbfsBlock")
os.Exit(1)
}
err = (&controllers.WorkspaceItemReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("WorkspaceItem"),
Recorder: mgr.GetEventRecorderFor("workspaceitem-controller"),
APIClient: apiClient,
}).SetupWithManager(mgr)
if err != nil {
setupLog.Error(err, "unable to create controller", "controller", "WorkspaceItem")
os.Exit(1)
}
// +kubebuilder:scaffold:builder
setupLog.Info("starting manager")