Skip to content

Commit

Permalink
GoCSI
Browse files Browse the repository at this point in the history
This patch provides a Go-based CSI client and server capable of
supporting additional storage platforms at runtime via Go plug-ins.
  • Loading branch information
akutz committed Jun 27, 2017
1 parent a4b578a commit 24161f9
Show file tree
Hide file tree
Showing 225 changed files with 71,686 additions and 2 deletions.
29 changes: 29 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
go_import_path: github.com/container-storage-interface/examples

language: go

go:
- 1.8.x

env:
global:
- CSI_ENDPOINT=tcp://127.0.0.1:4210

before_install:
- curl -LO https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip
- unzip protoc-3.3.0-linux-x86_64.zip
- chmod +x bin/protoc
- export PATH="$(pwd)/bin:$PATH"

install:
- go get -u github.com/golang/protobuf/proto
- go get -u github.com/golang/protobuf/protoc-gen-go
- go get -u google.golang.org/grpc
- make -C gocsi goget

script:
- make -C gocsi build
- make -C gocsi test

after_failure:
- if [ -e "gocsi/csd.log" ]; then cat gocsi/csd.log; fi
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# examples
Reference plugin implementations
# Container Storage Interface (CSI) Examples [![build status](https://travis-ci.org/container-storage-interface/examples.svg?branch=master)](https://travis-ci.org/container-storage-interface/examples)
This project contains examples CSI examples.

| Name | Description |
|------|-------------|
| [gocsi](./gocsi) | A Go-based CSI client and server capable of supporting additional storage platforms at runtime via Go plug-ins. |
4 changes: 4 additions & 0 deletions gocsi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.build/
.DS_Store
csd.log
mock.so
59 changes: 59 additions & 0 deletions gocsi/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
SHELL := $(shell env which bash)

all: build

# load the go bits
include go.mk

# configure and load the csi protobuf generator
CSI_PROTO_DIR := csi
include csi.mk

# the name of the program being built
PROG := $(PKG_DIR_GO)/$(IMPORT_PATH).a

# the target for building the gocsi library
$(PROG):$(CSI_GOSRC) \
$(filter-out %_test.go,$(wildcard *.go)) \
| $(GOGET) $(PKG_DIR_GO)
go install -pkgdir $(abspath $(PKG_DIR_GO))
@echo $@

# a list of sub-projects to make
SUB_PROJS := $(subst /,,$(dir $(wildcard */Makefile)))

# prints a list of the projects to make
projs:
@echo . $(SUB_PROJS)

build: $(PROG)
$(foreach d,$(SUB_PROJS),$(MAKE) -C $d $@;)

clean:
go clean -i
$(foreach d,$(SUB_PROJS),$(MAKE) -C $d $@;)

clobber: clean
rm -fr $(CSI_PROTO_DIR) $(BUILD_DIR)
$(foreach d,$(SUB_PROJS),$(MAKE) -C $d $@;)

goget: $(GOGET)
$(foreach d,$(SUB_PROJS),$(MAKE) -C $d $@;)

test: build
@rm -f csd.log
CSI_PLUGINS=$$(pwd)/csp/moc/moc-csi-plugin.so csd/csd mock > csd.log 2>&1 &
@until grep 'mock\.Serve' csd.log > /dev/null 2>&1; do sleep 0.1; done;
csc/csc ls
csc/csc new -o norootsquash,uid=500,gid=500 \
-t ext4 -requiredBytes 107374182400 \
-params color=purple,up=down \
"My New Volume"
csc/csc ls
kill $$(ps aux | grep '[c]sd' | awk '{print $$2}')
cat csd.log

benchmark: $(PROG)
go test -benchmem -parallel 100 -bench .

.PHONY: projs build clean clobber goget vendor test benchmark
153 changes: 153 additions & 0 deletions gocsi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# gocsi
This project provides a Go-based CSI client and server capable of
supporting additional storage platforms at runtime via Go plug-ins.

| Name | Description |
|------|-------------|
| [`csc`](./csc) | A command line interface (CLI) tool that provides analogues for all of the CSI RPCs. |
| [`csd`](./csd) | A CLI tool that serves the CSI services -- Controller, Identity, and Node -- on a TCP port or UNIX socket. The storage platform support for `csd` is provided via Go plug-ins, loadable at runtime. |
| [`csp`](./csp) | A directory containing Go packages that can be built as stand-alone CLI programs capable of serving the CSI services via a TCP port or UNIX socket, similar to `csd`. Additionally, the `csp` packages can **also** be built as Go plug-ins that the `csd` program can load, extending its storage provider support dynamically, at runtime.

## Getting Started
The example below illustrates how the above tools and Go plug-ins
work together to provide a cohesive experience in line with the CSI
specification.

Please note that the mock `csp` is used to fulfill the role of a
storage platform. Also, because Go plug-ins are demonstrated this
example must be executed on a Linux platform as Go plug-ins are not
yet supported by other operating systems (OS).

```bash
# get and install the sources
$ go get github.com/container-storage-interface/examples/gocsi

# build the container storage client
$ go install github.com/container-storage-interface/examples/gocsi/csc

# build the container storage daemon
$ go install github.com/container-storage-interface/examples/gocsi/csd

# build the mock container storage provider
$ go build -o mock.so -buildmode plugin github.com/container-storage-interface/examples/gocsi/csp/moc

# export the CSI endpoint
$ export CSI_ENDPOINT=tcp://127.0.0.1:8080

# start the server (assuming $GOPATH/bin is in the PATH)
$ CSI_PLUGINS=$(pwd)/mock.so csd mock > csd.log 2>&1 &
[1] 19050

# use the client to ask for a list of volumes
$ csc ls
id=1 name=Mock Volume 1
id=2 name=Mock Volume 2
id=3 name=Mock Volume 3

# create a new volume
$ csc new "My New Volume"
id=4 name=My New Volume

# query the volume list again
$ csc ls
id=1 name=Mock Volume 1
id=2 name=Mock Volume 2
id=3 name=Mock Volume 3
id=4 name=My New Volume

# kill the server
kill -HUP $(ps aux | grep '[c]sd' | awk '{print $2}')

# view the server log
$ cat csd.log
2017/06/26 01:54:48 loaded plug-in: mock.so
2017/06/26 01:54:48 registered endpoint: mock
2017/06/26 01:54:48 mock.Serve
2017/06/26 01:55:36 csd.ListVolumes
2017/06/26 01:55:36 ...Volume.ID=1
2017/06/26 01:55:36 ...Volume.ID=2
2017/06/26 01:55:36 ...Volume.ID=3
2017/06/26 01:55:47 csd.CreateVolume
2017/06/26 01:55:47 CreateVolume.CapacityRange=<nil>
2017/06/26 01:55:47 CreateVolume.Name=My New Volume
2017/06/26 01:55:47 CreateVolume.Parameters=map[]
2017/06/26 01:55:47 CreateVolume.VolumeCapabilities=[]
2017/06/26 01:55:47 ...Volume.ID=4
2017/06/26 01:56:04 csd.ListVolumes
2017/06/26 01:56:04 ...Volume.ID=1
2017/06/26 01:56:04 ...Volume.ID=2
2017/06/26 01:56:04 ...Volume.ID=3
2017/06/26 01:56:04 ...Volume.ID=4
received signal: terminated: shutting down
server stopped gracefully
```

## Build Reference
GoCSI can be built entirely using `go build` and `go install`. However,
Make is also used in order to create a more deterministic and portable
build process.

| Make Target | Description |
|-------------|-------------|
| `build` | Builds `csc`, `csd`, and all of the `csp`s. Please note that this target does not install anything to the `$GOPATH`. Instead each of the packages' own Makefiles ensure that all code is built in a directory named `.build` inside the package using the `go` tool's `-pkgdir` flag in conjunction with the environment variable `GOBIN`. This ensures builds that are easy to clean. |
| `clean` | Removes the artifacts produced by the `build` target. |
| `clobber` | Depends on `clean`. Removes all generated sources and vendored dependencies. |
| `test` | Depends on `build`. Executes an end-to-end example using `csc`, `csd`, and the mock `csp`.
| `bencmark` | Usses `go test` to illustrate the performance of the `PipeConn` used by `csd` to host the `csp`s as in-memory CSI endpoints. |
| `goget` | Will scan the packages and execute `go get` for any missing dependencies. |


## Frequently Asked Questions
This section answers some of the common inquiries related to this project.

**What is the purpose of GoCSI?**

The purpose of GoCSI is not to provide a reference library or set
of tools meant to be used by other, third-party or external consumers
wishing to jumpstart CSI development. GoCSI is simply a series of small
programs and packages that provide working examples and demonstrations
of the CSI specification. If at some point the CSI working group wishes
to build a set of reference implementations based on the contents of
this project, then that is fine. If GoCSI stands forever as simply a
series of examples, that's fine as well.

**Why vendor Proto and gRPC in the `csp`s?**

There are two answers to this question. The first is simply so that
the examples included in GoCSI can be built/executed with standard
Go patterns without the need for an external build system such as
[Make](https://www.gnu.org/software/make/) or vendoring solution like
[Glide](https://glide.sh/). The fact is that executing `make clobber`
at the root of the GoCSI project will remove all of the vendored
code as well as generated sources. Running `make` again will restore
all of the removed code. It's simply by design to leave that code
as part of the commit in order to make it as easy as possible to use
the examples included in GoCSI.

The second answer is the result of weeks of research and trial and
error. The short answer has to do with how Go manages packages, plug-ins,
and the gRPC type registry. The long answer is summarized by
[golang/go#20481](https://github.com/golang/go/issues/20481).

The purpose of the `csp`s is to demonstrate the triviality of creating a
package that can be built both as a stand-alone CSI endpoint and a Go
plug-in, capable of providing support for a new storage platform to
a separate, completely unrelated host program. Therefore the `csp`s
should have absolutely no relationship to any other GoCSI project,
even `csd`, the program capable of loading the `csp`s as Go plug-ins.

Truthfully the matter is much more detailed, but it won't be repeated
here. For that please see the aforementioned Golang issue. Suffice
it to say, the only way to create Go plug-ins that are useful in
production software is to 1) vendor everything inside the plug-in's
package and 2) use only Go stdlib types and empty interfaces when
communicating with the plug-in from the host program.

**Why generate Go sources from the CSI protobuf in multiple locations?**

The answer to this question is similar to the answer of the previous
question. If the `csp`s referenced the `csi` package at the root of the
GoCSI project then the `csd` program would not be able to load the Go
plug-ins due to 1) duplicate gRPC type registrations and 2) a possibly
different hash for the referenced package since the plug-ins are built
separately and thus potentially from different sources.
Loading

0 comments on commit 24161f9

Please sign in to comment.