mirror of
https://github.com/ceph/go-ceph
synced 2024-12-18 04:15:42 +00:00
66fe3724d2
Add some prose to the hints doc on how to use our docs convention to map a C/command line api to a go-ceph api. Note how to make use of a {Mon,Mgr}Command API JSON-command without go-ceph code. Note how apis start as preview before becoming stable. Signed-off-by: John Mulligan <jmulligan@redhat.com>
256 lines
9.3 KiB
Markdown
256 lines
9.3 KiB
Markdown
# API Hints & Quick How-Tos
|
|
|
|
Below you'll find some brief sections that show how some of the API calls
|
|
in go-ceph work together. This is not meant to cover every possible use
|
|
case but are recorded here as a quick way to get familiar with these
|
|
calls.
|
|
|
|
|
|
## General
|
|
|
|
### Finding an API
|
|
|
|
The go-ceph project wraps existing APIs that are part of Ceph. There are two
|
|
kinds of APIs that are wrapped. The first style of API is based on functions
|
|
Ceph exports as client libraries in C. The three packages in go-ceph `cephfs`,
|
|
`rados`, and `rbd` map to `libcephfs`, `librados`, and `librbd` respectively.
|
|
|
|
The go-ceph packages that wrap Ceph C APIs follow a documentation convention
|
|
that aims to make it easier to map between the APIs. In functions that are
|
|
implemented using a certain C API function a line with the term `Implements:`
|
|
will be followed by the C function's declaration - matching what can be found
|
|
in the C library's header file. For example, if you knew you wanted to wrap the
|
|
rbd function to get an image's metadata, `rbd_metadata_get`, you could search
|
|
within the source code or https://pkg.go.dev/github.com/ceph/go-ceph/rbd for
|
|
`rbd_metadata_get` which would lead you to the `GetMetadata` method of the
|
|
`Image` type.
|
|
|
|
The second style of API is based on functions implemented within Ceph services
|
|
based on Ceph's "command" system. These functions are primarily accessed using
|
|
the `ceph` command. Many of the functions within the ceph command
|
|
are implemented by sending a structured JSON message to either the Ceph MON or
|
|
MGR. Packages in go-ceph that wrap these sorts of APIs are found in
|
|
`cephfs/admin`, `rbd/admin`, and `common/admin/manager` for example.
|
|
|
|
The command/JSON based API packages follow a different, but similar
|
|
documentation convention to the C based APIs. Functions that roughly map to a
|
|
particular `ceph` CLI command will contain a line with the term `Similar To:`
|
|
followed by the `ceph` command it is similar to. For example, if
|
|
you knew you wanted to create a CephFS subvolume group and would normally use
|
|
the command `ceph fs subvolumegroup create` to do so, you could search within
|
|
the source code or https://pkg.go.dev/github.com/ceph/go-ceph/cephfs/admin for
|
|
`ceph fs subvolumegroup create` which would lead you to the
|
|
`CreateSubVolumeGroup` property of the `FSAdmin` type.
|
|
|
|
#### Can't find the API you want?
|
|
|
|
The go-ceph project is maintained separately from Ceph and it is common for
|
|
APIs to be added to Ceph that are not present in go-ceph. Sometimes we resolve
|
|
the differences quickly but not always.
|
|
|
|
Generally, there is no simple way to access a C based API from Go without
|
|
updates to the code. If there's an API that you need that doesn't appear to be
|
|
wrapped by go-ceph, please [file an
|
|
issue](https://github.com/ceph/go-ceph/issues). If you are comfortable writing
|
|
Go and would like to try writing a wrapper function we're more than happy to
|
|
[welcome contributions](./development.md#contribution-guidelines) as well.
|
|
|
|
The command/JSON based APIs can be accessed without directly wrapping them.
|
|
The large majority of these functions are based on either
|
|
[rados.MgrCommand](https://pkg.go.dev/github.com/ceph/go-ceph@v0.21.0/rados#Conn.MgrCommand)
|
|
or
|
|
[rados.MonCommand](https://pkg.go.dev/github.com/ceph/go-ceph@v0.21.0/rados#Conn.MonCommand).
|
|
Both these functions accept a formatted JSON object that maps to a command line
|
|
constant prefix and the variable argument values. Determining the JSON prefix
|
|
and accepted arguments can be done using a special JSON-command: `{"prefix":
|
|
"get_command_descriptions"}`. This will return a dump of all commands the
|
|
service knows about.
|
|
|
|
You can then use this information to construct your own JSON to send to
|
|
the server. For example the prefix `fs subvolumegroup ls` takes an argument `vol_name`
|
|
which is annotated as a CephString. Thus you can send the JSON
|
|
`{"prefix": "fs subvolumegroup ls", "vol_name": "foobar":, "format": "json"}`.
|
|
The last parameter `format` is a special general argument that *suggests* to the
|
|
server that you want the reply data to be JSON formatted.
|
|
|
|
That all said, while you can directly interact with the command/JSON based APIs we
|
|
are also very happy to consider feature requests as well as contributions to make
|
|
working with these API more Go-idiomatic, convenient, and common.
|
|
|
|
#### Preview APIs
|
|
|
|
When a new API is added to go-ceph we consider the API to be a "preview" API.
|
|
This means that while we think the API is good enough to distribute we do not
|
|
promise not to change it. We assume that most consumers of go-ceph want a
|
|
stable API and so the preview APIs are "hidden" behind a go build tag. This
|
|
tag, `ceph_preview`, can be passed to the Go build command such that when you
|
|
import go-ceph packages the preview APIs will become "visible" to your code.
|
|
Do be aware that if you use preview APIs in your code there is the chance
|
|
they'll change between go-ceph releases.
|
|
|
|
Preview APIs do not show up on pkg.go.dev but we do list all of them in
|
|
our [API status document](./api-status.md). We track when each API was
|
|
added and when the API is expected to become stable.
|
|
|
|
|
|
## rados Package
|
|
|
|
### Connecting to a cluster
|
|
|
|
Connect to a Ceph cluster using a configuration file located in the default
|
|
search paths.
|
|
|
|
```go
|
|
conn, _ := rados.NewConn()
|
|
conn.ReadDefaultConfigFile()
|
|
conn.Connect()
|
|
```
|
|
|
|
A connection can be shutdown by calling the `Shutdown` method on the
|
|
connection object (e.g. `conn.Shutdown()`). There are also other methods for
|
|
configuring the connection. Specific configuration options can be set:
|
|
|
|
```go
|
|
conn.SetConfigOption("log_file", "/dev/null")
|
|
```
|
|
|
|
and command line options can also be used using the `ParseCmdLineArgs` method.
|
|
|
|
```go
|
|
args := []string{ "--mon-host", "1.1.1.1" }
|
|
err := conn.ParseCmdLineArgs(args)
|
|
```
|
|
|
|
For other configuration options see the full documentation.
|
|
|
|
### Object I/O
|
|
|
|
Object in RADOS can be written to and read from with through an interface very
|
|
similar to a standard file I/O interface:
|
|
|
|
```go
|
|
// open a pool handle
|
|
ioctx, err := conn.OpenIOContext("mypool")
|
|
|
|
// write some data
|
|
bytesIn := []byte("input data")
|
|
err = ioctx.Write("obj", bytesIn, 0)
|
|
|
|
// read the data back out
|
|
bytesOut := make([]byte, len(bytesIn))
|
|
_, err := ioctx.Read("obj", bytesOut, 0)
|
|
|
|
if !bytes.Equal(bytesIn, bytesOut) {
|
|
fmt.Println("Output is not input!")
|
|
}
|
|
```
|
|
|
|
### Pool maintenance
|
|
|
|
The list of pools in a cluster can be retrieved using the `ListPools` method
|
|
on the connection object. On a new cluster the following code snippet:
|
|
|
|
```go
|
|
pools, _ := conn.ListPools()
|
|
fmt.Println(pools)
|
|
```
|
|
|
|
will produce the output `[data metadata rbd]`, along with any other pools that
|
|
might exist in your cluster. Pools can also be created and destroyed. The
|
|
following creates a new, empty pool with default settings.
|
|
|
|
```go
|
|
conn.MakePool("new_pool")
|
|
```
|
|
|
|
Deleting a pool is also easy. Call `DeletePool(name string)` on a connection object to
|
|
delete a pool with the given name. The following will delete the pool named
|
|
`new_pool` and remove all of the pool's data.
|
|
|
|
```go
|
|
conn.DeletePool("new_pool")
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
As typical of Go codebases, a large number of functions in go-ceph return `error`s.
|
|
Some of these errors are based on non-exported types. This is deliberate choice.
|
|
However, much of the relevant data these types can contain are available. One
|
|
does not have to resort to the somewhat brittle approach of converting errors
|
|
to strings and matching on (parts of) said string.
|
|
|
|
In some cases the errors returned by calls are considered "sentinel" errors.
|
|
These errors can be matched to exported values in the package using the
|
|
`errors.Is` function from the Go standard library.
|
|
|
|
Example:
|
|
```go
|
|
// we want to delete a pool, but oops, conn is disconnected
|
|
err := conn.DeletePool("foo")
|
|
if err != nil {
|
|
if errors.Is(err, rados.ErrNotConnected) {
|
|
// ... do something specific when not connected ...
|
|
} else {
|
|
// ... handle generic error ...
|
|
}
|
|
}
|
|
```
|
|
|
|
Example:
|
|
```go
|
|
err := rgw.MyAPICall()
|
|
if err != nil {
|
|
if errors.Is(err, rgw.ErrInvalidAccessKey) {
|
|
// ... do something specific to access errors ...
|
|
} else if errors.Is(err, rgw.ErrNoSuchUser) {
|
|
// ... do something specific to user not existing ...
|
|
} else {
|
|
// ... handle generic error ...
|
|
}
|
|
}
|
|
```
|
|
|
|
In other cases the returned error doesn't match a specific error value but
|
|
rather is implemented by a type that may carry additional data. Specifically,
|
|
many errors in go-ceph implement an `ErrorCode() int` method. If this is the
|
|
case you can use ErrorCode to access a numeric error code provided by calls to
|
|
Ceph. Note that the error codes returned by Ceph often match unix/linux
|
|
`errno`s - but the exact meaning of the values returned by `ErrorCode()` are
|
|
determined by the Ceph APIs and go-ceph is just making them accessible.
|
|
|
|
|
|
Example:
|
|
```go
|
|
type errorWithCode interface {
|
|
ErrorCode() int
|
|
}
|
|
|
|
err := rados.SomeRadosFunc()
|
|
if err != nil {
|
|
var ec errorWithCode
|
|
if errors.As(err, &ec) {
|
|
errCode := ec.ErrorCode()
|
|
// ... do something with errCode ...
|
|
} else {
|
|
// ... handle generic error ...
|
|
}
|
|
}
|
|
```
|
|
|
|
Note that Go allows type definitions inline so you can even write:
|
|
```go
|
|
err := rados.SomeRadosFunc()
|
|
if err != nil {
|
|
var ec interface { ErrorCode() int }
|
|
if errors.As(err, &ec) {
|
|
errCode := ec.ErrorCode()
|
|
// ... do something with errCode ...
|
|
} else {
|
|
// ... handle generic error ...
|
|
}
|
|
}
|
|
```
|
|
|
|
Newer packages in go-ceph generally prefer to latter approach to avoid creating
|
|
lots of sentinels that are only used rarely.
|