Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions existingvolumebroker.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type BrokerType int
const (
BrokerTypeNFS BrokerType = iota
BrokerTypeSMB
BrokerTypeBlock
)

type Broker struct {
Expand Down Expand Up @@ -87,6 +88,10 @@ func (b *Broker) isNFSBroker() bool {
return b.brokerType == BrokerTypeNFS
}

func (b *Broker) isBlockStorageBroker() bool {
return b.brokerType == BrokerTypeBlock
}

func (b *Broker) Services(_ context.Context) ([]domain.Service, error) {
logger := b.logger.Session("services")
logger.Info("start")
Expand All @@ -100,16 +105,17 @@ func (b *Broker) Provision(context context.Context, instanceID string, details d
logger.Info("start")
defer logger.Info("end")

var configuration map[string]interface{}

var decoder = json.NewDecoder(bytes.NewBuffer(details.RawParameters))
err := decoder.Decode(&configuration)
if err != nil {
return domain.ProvisionedServiceSpec{}, apiresponses.ErrRawParamsInvalid
configuration := map[string]interface{}{}
if details.RawParameters != nil {
var decoder = json.NewDecoder(bytes.NewBuffer(details.RawParameters))
err := decoder.Decode(&configuration)
if err != nil {
return domain.ProvisionedServiceSpec{}, apiresponses.ErrRawParamsInvalid
}
}

share := stringifyShare(configuration[SHARE_KEY])
if share == "" {
if share == "" && !b.isBlockStorageBroker() {
return domain.ProvisionedServiceSpec{}, errors.New("config requires a \"share\" key")
}

Expand Down Expand Up @@ -147,7 +153,7 @@ func (b *Broker) Provision(context context.Context, instanceID string, details d
return domain.ProvisionedServiceSpec{}, apiresponses.ErrInstanceAlreadyExists
}

err = b.store.CreateInstanceDetails(instanceID, instanceDetails)
err := b.store.CreateInstanceDetails(instanceID, instanceDetails)
if err != nil {
return domain.ProvisionedServiceSpec{}, fmt.Errorf("failed to store instance details: %s", err.Error())
}
Expand Down Expand Up @@ -257,6 +263,7 @@ func (b *Broker) Bind(context context.Context, instanceID string, bindingID stri
return domain.Binding{}, err
}

deviceType := "shared"
driverName := "smbdriver"
if b.isNFSBroker() {
driverName = "nfsv3driver"
Expand All @@ -268,6 +275,14 @@ func (b *Broker) Bind(context context.Context, instanceID string, bindingID stri
// see (https://github.com/cloudfoundry/nfsv3driver/blob/ac1e1d26fec9a8551cacfabafa6e035f233c83e0/mapfs_mounter.go#L121)
mountOpts[SOURCE_KEY] = fmt.Sprintf("nfs://%s", mountOpts[SOURCE_KEY])
}
if b.isBlockStorageBroker() {
deviceType = "dedicated"
driverName = "blockstoragedriver"

// for backwards compatibility, because the driver infrastructure expects a source,
// we add one
mountOpts[SOURCE_KEY] = "ignored"
}

logger.Debug("volume-service-binding", lager.Data{"driver": driverName, "mountOpts": mountOpts})

Expand All @@ -290,7 +305,7 @@ func (b *Broker) Bind(context context.Context, instanceID string, bindingID stri
ContainerDir: evaluateContainerPath(opts, instanceID),
Mode: mode,
Driver: driverName,
DeviceType: "shared",
DeviceType: deviceType,
Device: domain.SharedDevice{
VolumeId: volumeId,
MountConfig: mountConfig,
Expand Down
157 changes: 157 additions & 0 deletions existingvolumebroker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"regexp"

"code.cloudfoundry.org/existingvolumebroker"
"code.cloudfoundry.org/existingvolumebroker/fakes"
Expand All @@ -19,6 +20,7 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gstruct"
"github.com/pivotal-cf/brokerapi/v11/domain"
"github.com/pivotal-cf/brokerapi/v11/domain/apiresponses"
)
Expand Down Expand Up @@ -532,6 +534,13 @@ var _ = Describe("Broker", func() {
Expect(binding.VolumeMounts[0].Mode).To(Equal("rw"))
})

It("uses shared as its device type", func() {
binding, err := broker.Bind(ctx, "some-instance-id", "binding-id", bindDetails, false)
Expect(err).NotTo(HaveOccurred())

Expect(binding.VolumeMounts[0].DeviceType).To(Equal("shared"))
})

It("errors if mode is not a boolean", func() {
var err error

Expand Down Expand Up @@ -1545,6 +1554,13 @@ var _ = Describe("Broker", func() {
Expect(binding.VolumeMounts[0].Device.MountConfig).To(HaveKeyWithValue("ro", "true"))
})

It("uses shared as its device type", func() {
binding, err := broker.Bind(ctx, "some-instance-id", "binding-id", bindDetails, false)
Expect(err).NotTo(HaveOccurred())

Expect(binding.VolumeMounts[0].DeviceType).To(Equal("shared"))
})

It("should write state", func() {
previousSaveCallCount := fakeStore.SaveCallCount()

Expand Down Expand Up @@ -2047,4 +2063,145 @@ var _ = Describe("Broker", func() {
})
})
})

Context("when the broker type is block", func() {
BeforeEach(func() {
fakeServices.ListReturns([]domain.Service{
{
ID: "block-service-id",
Name: "block",
Description: "On-demand disk",
Bindable: true,
PlanUpdatable: false,
Tags: []string{"block"},
Requires: []domain.RequiredPermission{"volume_mount"},
Plans: []domain.ServicePlan{
{
Name: "on-demand",
ID: "on-demand",
Description: "On-demand disk",
},
},
},
})

configMask, err := vmo.NewMountOptsMask(
[]string{
"ro",
},
map[string]interface{}{},
map[string]string{
"readonly": "ro",
},
[]string{},
[]string{},
)
Expect(err).NotTo(HaveOccurred())

broker = existingvolumebroker.New(
existingvolumebroker.BrokerTypeBlock,
logger,
fakeServices,
fakeOs,
nil,
fakeStore,
configMask,
)
})

Context(".Services", func() {
It("returns the service catalog as appropriate", func() {
results, err := broker.Services(ctx)
Expect(err).NotTo(HaveOccurred())

Expect(results).To(HaveLen(1))

result := results[0]
Expect(result.ID).To(Equal("block-service-id"))
Expect(result.Name).To(Equal("block"))
Expect(result.Description).To(Equal("On-demand disk"))
Expect(result.Bindable).To(Equal(true))
Expect(result.PlanUpdatable).To(Equal(false))
Expect(result.Tags).To(ConsistOf([]string{"block"}))
Expect(result.Requires).To(ContainElement(domain.RequiredPermission("volume_mount")))

Expect(result.Plans[0].Name).To(Equal("on-demand"))
Expect(result.Plans[0].ID).To(Equal("on-demand"))
Expect(result.Plans[0].Description).To(Equal("On-demand disk"))
})
})

Context(".Provision", func() {
var (
instanceID string
provisionDetails domain.ProvisionDetails
asyncAllowed bool

err error
)

BeforeEach(func() {
instanceID = "some-instance-id"

provisionDetails = domain.ProvisionDetails{PlanID: "on-demand"}
asyncAllowed = false
})

JustBeforeEach(func() {
_, err = broker.Provision(ctx, instanceID, provisionDetails, asyncAllowed)
})

It("should not error", func() {
By("not requiring a share (unlike nfs/smb", func() {
Expect(err).NotTo(HaveOccurred())
})
})
})

Context(".Bind", func() {
var (
instanceID, serviceID string
bindDetails domain.BindDetails
bindParameters map[string]interface{}
)

BeforeEach(func() {
instanceID = "some-instance-id"
fakeStore.RetrieveInstanceDetailsStub = func(instanceID string) (brokerstore.ServiceInstance, error) {
return brokerstore.ServiceInstance{
ServiceID: serviceID,
ServiceFingerPrint: map[string]interface{}{},
}, nil
}

bindParameters = map[string]interface{}{}
bindMessage, err := json.Marshal(bindParameters)
Expect(err).NotTo(HaveOccurred())

bindDetails = domain.BindDetails{
AppGUID: "guid",
RawParameters: bindMessage,
}
})

It("should return a dedicated device type", func() {
binding, err := broker.Bind(ctx, instanceID, "binding-id", bindDetails, false)
Expect(err).NotTo(HaveOccurred())

Expect(len(binding.VolumeMounts)).To(Equal(1))
Expect(binding.VolumeMounts[0]).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{
"Driver": Equal("blockstoragedriver"),
"ContainerDir": Equal("/var/vcap/data/some-instance-id"),
"Mode": Equal("rw"),
"DeviceType": Equal("dedicated"),
"Device": gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{
"VolumeId": MatchRegexp(fmt.Sprintf(`^%s-[a-f0-9]{32}$`, regexp.QuoteMeta(instanceID))),
"MountConfig": gstruct.MatchAllKeys(gstruct.Keys{
"source": Equal("ignored"),
}),
}),
}))
})
})
})
})
Loading