client.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package containerd
  14. import (
  15. "bytes"
  16. "context"
  17. "encoding/json"
  18. "fmt"
  19. "net/http"
  20. "runtime"
  21. "strconv"
  22. "strings"
  23. "sync"
  24. "time"
  25. containersapi "github.com/containerd/containerd/api/services/containers/v1"
  26. contentapi "github.com/containerd/containerd/api/services/content/v1"
  27. diffapi "github.com/containerd/containerd/api/services/diff/v1"
  28. eventsapi "github.com/containerd/containerd/api/services/events/v1"
  29. imagesapi "github.com/containerd/containerd/api/services/images/v1"
  30. introspectionapi "github.com/containerd/containerd/api/services/introspection/v1"
  31. leasesapi "github.com/containerd/containerd/api/services/leases/v1"
  32. namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1"
  33. snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
  34. "github.com/containerd/containerd/api/services/tasks/v1"
  35. versionservice "github.com/containerd/containerd/api/services/version/v1"
  36. "github.com/containerd/containerd/containers"
  37. "github.com/containerd/containerd/content"
  38. contentproxy "github.com/containerd/containerd/content/proxy"
  39. "github.com/containerd/containerd/defaults"
  40. "github.com/containerd/containerd/errdefs"
  41. "github.com/containerd/containerd/events"
  42. "github.com/containerd/containerd/images"
  43. "github.com/containerd/containerd/leases"
  44. leasesproxy "github.com/containerd/containerd/leases/proxy"
  45. "github.com/containerd/containerd/namespaces"
  46. "github.com/containerd/containerd/pkg/dialer"
  47. "github.com/containerd/containerd/platforms"
  48. "github.com/containerd/containerd/plugin"
  49. "github.com/containerd/containerd/remotes"
  50. "github.com/containerd/containerd/remotes/docker"
  51. "github.com/containerd/containerd/services/introspection"
  52. "github.com/containerd/containerd/snapshots"
  53. snproxy "github.com/containerd/containerd/snapshots/proxy"
  54. "github.com/containerd/typeurl"
  55. ptypes "github.com/gogo/protobuf/types"
  56. ocispec "github.com/opencontainers/image-spec/specs-go/v1"
  57. specs "github.com/opencontainers/runtime-spec/specs-go"
  58. "github.com/pkg/errors"
  59. "google.golang.org/grpc"
  60. "google.golang.org/grpc/backoff"
  61. "google.golang.org/grpc/health/grpc_health_v1"
  62. )
  63. func init() {
  64. const prefix = "types.containerd.io"
  65. // register TypeUrls for commonly marshaled external types
  66. major := strconv.Itoa(specs.VersionMajor)
  67. typeurl.Register(&specs.Spec{}, prefix, "opencontainers/runtime-spec", major, "Spec")
  68. typeurl.Register(&specs.Process{}, prefix, "opencontainers/runtime-spec", major, "Process")
  69. typeurl.Register(&specs.LinuxResources{}, prefix, "opencontainers/runtime-spec", major, "LinuxResources")
  70. typeurl.Register(&specs.WindowsResources{}, prefix, "opencontainers/runtime-spec", major, "WindowsResources")
  71. }
  72. // New returns a new containerd client that is connected to the containerd
  73. // instance provided by address
  74. func New(address string, opts ...ClientOpt) (*Client, error) {
  75. var copts clientOpts
  76. for _, o := range opts {
  77. if err := o(&copts); err != nil {
  78. return nil, err
  79. }
  80. }
  81. if copts.timeout == 0 {
  82. copts.timeout = 10 * time.Second
  83. }
  84. c := &Client{
  85. defaultns: copts.defaultns,
  86. }
  87. if copts.defaultRuntime != "" {
  88. c.runtime = copts.defaultRuntime
  89. } else {
  90. c.runtime = defaults.DefaultRuntime
  91. }
  92. if copts.defaultPlatform != nil {
  93. c.platform = copts.defaultPlatform
  94. } else {
  95. c.platform = platforms.Default()
  96. }
  97. if copts.services != nil {
  98. c.services = *copts.services
  99. }
  100. if address != "" {
  101. backoffConfig := backoff.DefaultConfig
  102. backoffConfig.MaxDelay = 3 * time.Second
  103. connParams := grpc.ConnectParams{
  104. Backoff: backoffConfig,
  105. }
  106. gopts := []grpc.DialOption{
  107. grpc.WithBlock(),
  108. grpc.WithInsecure(),
  109. grpc.FailOnNonTempDialError(true),
  110. grpc.WithConnectParams(connParams),
  111. grpc.WithContextDialer(dialer.ContextDialer),
  112. // TODO(stevvooe): We may need to allow configuration of this on the client.
  113. grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
  114. grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
  115. }
  116. if len(copts.dialOptions) > 0 {
  117. gopts = copts.dialOptions
  118. }
  119. if copts.defaultns != "" {
  120. unary, stream := newNSInterceptors(copts.defaultns)
  121. gopts = append(gopts,
  122. grpc.WithUnaryInterceptor(unary),
  123. grpc.WithStreamInterceptor(stream),
  124. )
  125. }
  126. connector := func() (*grpc.ClientConn, error) {
  127. ctx, cancel := context.WithTimeout(context.Background(), copts.timeout)
  128. defer cancel()
  129. conn, err := grpc.DialContext(ctx, dialer.DialAddress(address), gopts...)
  130. if err != nil {
  131. return nil, errors.Wrapf(err, "failed to dial %q", address)
  132. }
  133. return conn, nil
  134. }
  135. conn, err := connector()
  136. if err != nil {
  137. return nil, err
  138. }
  139. c.conn, c.connector = conn, connector
  140. }
  141. if copts.services == nil && c.conn == nil {
  142. return nil, errors.Wrap(errdefs.ErrUnavailable, "no grpc connection or services is available")
  143. }
  144. // check namespace labels for default runtime
  145. if copts.defaultRuntime == "" && c.defaultns != "" {
  146. if label, err := c.GetLabel(context.Background(), defaults.DefaultRuntimeNSLabel); err != nil {
  147. return nil, err
  148. } else if label != "" {
  149. c.runtime = label
  150. }
  151. }
  152. return c, nil
  153. }
  154. // NewWithConn returns a new containerd client that is connected to the containerd
  155. // instance provided by the connection
  156. func NewWithConn(conn *grpc.ClientConn, opts ...ClientOpt) (*Client, error) {
  157. var copts clientOpts
  158. for _, o := range opts {
  159. if err := o(&copts); err != nil {
  160. return nil, err
  161. }
  162. }
  163. c := &Client{
  164. defaultns: copts.defaultns,
  165. conn: conn,
  166. runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS),
  167. }
  168. // check namespace labels for default runtime
  169. if copts.defaultRuntime == "" && c.defaultns != "" {
  170. if label, err := c.GetLabel(context.Background(), defaults.DefaultRuntimeNSLabel); err != nil {
  171. return nil, err
  172. } else if label != "" {
  173. c.runtime = label
  174. }
  175. }
  176. if copts.services != nil {
  177. c.services = *copts.services
  178. }
  179. return c, nil
  180. }
  181. // Client is the client to interact with containerd and its various services
  182. // using a uniform interface
  183. type Client struct {
  184. services
  185. connMu sync.Mutex
  186. conn *grpc.ClientConn
  187. runtime string
  188. defaultns string
  189. platform platforms.MatchComparer
  190. connector func() (*grpc.ClientConn, error)
  191. }
  192. // Reconnect re-establishes the GRPC connection to the containerd daemon
  193. func (c *Client) Reconnect() error {
  194. if c.connector == nil {
  195. return errors.Wrap(errdefs.ErrUnavailable, "unable to reconnect to containerd, no connector available")
  196. }
  197. c.connMu.Lock()
  198. defer c.connMu.Unlock()
  199. c.conn.Close()
  200. conn, err := c.connector()
  201. if err != nil {
  202. return err
  203. }
  204. c.conn = conn
  205. return nil
  206. }
  207. // IsServing returns true if the client can successfully connect to the
  208. // containerd daemon and the healthcheck service returns the SERVING
  209. // response.
  210. // This call will block if a transient error is encountered during
  211. // connection. A timeout can be set in the context to ensure it returns
  212. // early.
  213. func (c *Client) IsServing(ctx context.Context) (bool, error) {
  214. c.connMu.Lock()
  215. if c.conn == nil {
  216. c.connMu.Unlock()
  217. return false, errors.Wrap(errdefs.ErrUnavailable, "no grpc connection available")
  218. }
  219. c.connMu.Unlock()
  220. r, err := c.HealthService().Check(ctx, &grpc_health_v1.HealthCheckRequest{}, grpc.WaitForReady(true))
  221. if err != nil {
  222. return false, err
  223. }
  224. return r.Status == grpc_health_v1.HealthCheckResponse_SERVING, nil
  225. }
  226. // Containers returns all containers created in containerd
  227. func (c *Client) Containers(ctx context.Context, filters ...string) ([]Container, error) {
  228. r, err := c.ContainerService().List(ctx, filters...)
  229. if err != nil {
  230. return nil, err
  231. }
  232. var out []Container
  233. for _, container := range r {
  234. out = append(out, containerFromRecord(c, container))
  235. }
  236. return out, nil
  237. }
  238. // NewContainer will create a new container in container with the provided id
  239. // the id must be unique within the namespace
  240. func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) {
  241. ctx, done, err := c.WithLease(ctx)
  242. if err != nil {
  243. return nil, err
  244. }
  245. defer done(ctx)
  246. container := containers.Container{
  247. ID: id,
  248. Runtime: containers.RuntimeInfo{
  249. Name: c.runtime,
  250. },
  251. }
  252. for _, o := range opts {
  253. if err := o(ctx, c, &container); err != nil {
  254. return nil, err
  255. }
  256. }
  257. r, err := c.ContainerService().Create(ctx, container)
  258. if err != nil {
  259. return nil, err
  260. }
  261. return containerFromRecord(c, r), nil
  262. }
  263. // LoadContainer loads an existing container from metadata
  264. func (c *Client) LoadContainer(ctx context.Context, id string) (Container, error) {
  265. r, err := c.ContainerService().Get(ctx, id)
  266. if err != nil {
  267. return nil, err
  268. }
  269. return containerFromRecord(c, r), nil
  270. }
  271. // RemoteContext is used to configure object resolutions and transfers with
  272. // remote content stores and image providers.
  273. type RemoteContext struct {
  274. // Resolver is used to resolve names to objects, fetchers, and pushers.
  275. // If no resolver is provided, defaults to Docker registry resolver.
  276. Resolver remotes.Resolver
  277. // PlatformMatcher is used to match the platforms for an image
  278. // operation and define the preference when a single match is required
  279. // from multiple platforms.
  280. PlatformMatcher platforms.MatchComparer
  281. // Unpack is done after an image is pulled to extract into a snapshotter.
  282. // It is done simultaneously for schema 2 images when they are pulled.
  283. // If an image is not unpacked on pull, it can be unpacked any time
  284. // afterwards. Unpacking is required to run an image.
  285. Unpack bool
  286. // UnpackOpts handles options to the unpack call.
  287. UnpackOpts []UnpackOpt
  288. // Snapshotter used for unpacking
  289. Snapshotter string
  290. // SnapshotterOpts are additional options to be passed to a snapshotter during pull
  291. SnapshotterOpts []snapshots.Opt
  292. // Labels to be applied to the created image
  293. Labels map[string]string
  294. // BaseHandlers are a set of handlers which get are called on dispatch.
  295. // These handlers always get called before any operation specific
  296. // handlers.
  297. BaseHandlers []images.Handler
  298. // HandlerWrapper wraps the handler which gets sent to dispatch.
  299. // Unlike BaseHandlers, this can run before and after the built
  300. // in handlers, allowing operations to run on the descriptor
  301. // after it has completed transferring.
  302. HandlerWrapper func(images.Handler) images.Handler
  303. // ConvertSchema1 is whether to convert Docker registry schema 1
  304. // manifests. If this option is false then any image which resolves
  305. // to schema 1 will return an error since schema 1 is not supported.
  306. ConvertSchema1 bool
  307. // Platforms defines which platforms to handle when doing the image operation.
  308. // Platforms is ignored when a PlatformMatcher is set, otherwise the
  309. // platforms will be used to create a PlatformMatcher with no ordering
  310. // preference.
  311. Platforms []string
  312. // MaxConcurrentDownloads is the max concurrent content downloads for each pull.
  313. MaxConcurrentDownloads int
  314. // AllMetadata downloads all manifests and known-configuration files
  315. AllMetadata bool
  316. // ChildLabelMap sets the labels used to reference child objects in the content
  317. // store. By default, all GC reference labels will be set for all fetched content.
  318. ChildLabelMap func(ocispec.Descriptor) []string
  319. }
  320. func defaultRemoteContext() *RemoteContext {
  321. return &RemoteContext{
  322. Resolver: docker.NewResolver(docker.ResolverOptions{
  323. Client: http.DefaultClient,
  324. }),
  325. }
  326. }
  327. // Fetch downloads the provided content into containerd's content store
  328. // and returns a non-platform specific image reference
  329. func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (images.Image, error) {
  330. fetchCtx := defaultRemoteContext()
  331. for _, o := range opts {
  332. if err := o(c, fetchCtx); err != nil {
  333. return images.Image{}, err
  334. }
  335. }
  336. if fetchCtx.Unpack {
  337. return images.Image{}, errors.Wrap(errdefs.ErrNotImplemented, "unpack on fetch not supported, try pull")
  338. }
  339. if fetchCtx.PlatformMatcher == nil {
  340. if len(fetchCtx.Platforms) == 0 {
  341. fetchCtx.PlatformMatcher = platforms.All
  342. } else {
  343. var ps []ocispec.Platform
  344. for _, s := range fetchCtx.Platforms {
  345. p, err := platforms.Parse(s)
  346. if err != nil {
  347. return images.Image{}, errors.Wrapf(err, "invalid platform %s", s)
  348. }
  349. ps = append(ps, p)
  350. }
  351. fetchCtx.PlatformMatcher = platforms.Any(ps...)
  352. }
  353. }
  354. ctx, done, err := c.WithLease(ctx)
  355. if err != nil {
  356. return images.Image{}, err
  357. }
  358. defer done(ctx)
  359. img, err := c.fetch(ctx, fetchCtx, ref, 0)
  360. if err != nil {
  361. return images.Image{}, err
  362. }
  363. return c.createNewImage(ctx, img)
  364. }
  365. // Push uploads the provided content to a remote resource
  366. func (c *Client) Push(ctx context.Context, ref string, desc ocispec.Descriptor, opts ...RemoteOpt) error {
  367. pushCtx := defaultRemoteContext()
  368. for _, o := range opts {
  369. if err := o(c, pushCtx); err != nil {
  370. return err
  371. }
  372. }
  373. if pushCtx.PlatformMatcher == nil {
  374. if len(pushCtx.Platforms) > 0 {
  375. var ps []ocispec.Platform
  376. for _, platform := range pushCtx.Platforms {
  377. p, err := platforms.Parse(platform)
  378. if err != nil {
  379. return errors.Wrapf(err, "invalid platform %s", platform)
  380. }
  381. ps = append(ps, p)
  382. }
  383. pushCtx.PlatformMatcher = platforms.Any(ps...)
  384. } else {
  385. pushCtx.PlatformMatcher = platforms.All
  386. }
  387. }
  388. // Annotate ref with digest to push only push tag for single digest
  389. if !strings.Contains(ref, "@") {
  390. ref = ref + "@" + desc.Digest.String()
  391. }
  392. pusher, err := pushCtx.Resolver.Pusher(ctx, ref)
  393. if err != nil {
  394. return err
  395. }
  396. var wrapper func(images.Handler) images.Handler
  397. if len(pushCtx.BaseHandlers) > 0 {
  398. wrapper = func(h images.Handler) images.Handler {
  399. h = images.Handlers(append(pushCtx.BaseHandlers, h)...)
  400. if pushCtx.HandlerWrapper != nil {
  401. h = pushCtx.HandlerWrapper(h)
  402. }
  403. return h
  404. }
  405. } else if pushCtx.HandlerWrapper != nil {
  406. wrapper = pushCtx.HandlerWrapper
  407. }
  408. return remotes.PushContent(ctx, pusher, desc, c.ContentStore(), pushCtx.PlatformMatcher, wrapper)
  409. }
  410. // GetImage returns an existing image
  411. func (c *Client) GetImage(ctx context.Context, ref string) (Image, error) {
  412. i, err := c.ImageService().Get(ctx, ref)
  413. if err != nil {
  414. return nil, err
  415. }
  416. return NewImage(c, i), nil
  417. }
  418. // ListImages returns all existing images
  419. func (c *Client) ListImages(ctx context.Context, filters ...string) ([]Image, error) {
  420. imgs, err := c.ImageService().List(ctx, filters...)
  421. if err != nil {
  422. return nil, err
  423. }
  424. images := make([]Image, len(imgs))
  425. for i, img := range imgs {
  426. images[i] = NewImage(c, img)
  427. }
  428. return images, nil
  429. }
  430. // Restore restores a container from a checkpoint
  431. func (c *Client) Restore(ctx context.Context, id string, checkpoint Image, opts ...RestoreOpts) (Container, error) {
  432. store := c.ContentStore()
  433. index, err := decodeIndex(ctx, store, checkpoint.Target())
  434. if err != nil {
  435. return nil, err
  436. }
  437. ctx, done, err := c.WithLease(ctx)
  438. if err != nil {
  439. return nil, err
  440. }
  441. defer done(ctx)
  442. copts := []NewContainerOpts{}
  443. for _, o := range opts {
  444. copts = append(copts, o(ctx, id, c, checkpoint, index))
  445. }
  446. ctr, err := c.NewContainer(ctx, id, copts...)
  447. if err != nil {
  448. return nil, err
  449. }
  450. return ctr, nil
  451. }
  452. func writeIndex(ctx context.Context, index *ocispec.Index, client *Client, ref string) (d ocispec.Descriptor, err error) {
  453. labels := map[string]string{}
  454. for i, m := range index.Manifests {
  455. labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = m.Digest.String()
  456. }
  457. data, err := json.Marshal(index)
  458. if err != nil {
  459. return ocispec.Descriptor{}, err
  460. }
  461. return writeContent(ctx, client.ContentStore(), ocispec.MediaTypeImageIndex, ref, bytes.NewReader(data), content.WithLabels(labels))
  462. }
  463. // GetLabel gets a label value from namespace store
  464. // If there is no default label, an empty string returned with nil error
  465. func (c *Client) GetLabel(ctx context.Context, label string) (string, error) {
  466. ns, err := namespaces.NamespaceRequired(ctx)
  467. if err != nil {
  468. if c.defaultns == "" {
  469. return "", err
  470. }
  471. ns = c.defaultns
  472. }
  473. srv := c.NamespaceService()
  474. labels, err := srv.Labels(ctx, ns)
  475. if err != nil {
  476. return "", err
  477. }
  478. value := labels[label]
  479. return value, nil
  480. }
  481. // Subscribe to events that match one or more of the provided filters.
  482. //
  483. // Callers should listen on both the envelope and errs channels. If the errs
  484. // channel returns nil or an error, the subscriber should terminate.
  485. //
  486. // The subscriber can stop receiving events by canceling the provided context.
  487. // The errs channel will be closed and return a nil error.
  488. func (c *Client) Subscribe(ctx context.Context, filters ...string) (ch <-chan *events.Envelope, errs <-chan error) {
  489. return c.EventService().Subscribe(ctx, filters...)
  490. }
  491. // Close closes the clients connection to containerd
  492. func (c *Client) Close() error {
  493. c.connMu.Lock()
  494. defer c.connMu.Unlock()
  495. if c.conn != nil {
  496. return c.conn.Close()
  497. }
  498. return nil
  499. }
  500. // NamespaceService returns the underlying Namespaces Store
  501. func (c *Client) NamespaceService() namespaces.Store {
  502. if c.namespaceStore != nil {
  503. return c.namespaceStore
  504. }
  505. c.connMu.Lock()
  506. defer c.connMu.Unlock()
  507. return NewNamespaceStoreFromClient(namespacesapi.NewNamespacesClient(c.conn))
  508. }
  509. // ContainerService returns the underlying container Store
  510. func (c *Client) ContainerService() containers.Store {
  511. if c.containerStore != nil {
  512. return c.containerStore
  513. }
  514. c.connMu.Lock()
  515. defer c.connMu.Unlock()
  516. return NewRemoteContainerStore(containersapi.NewContainersClient(c.conn))
  517. }
  518. // ContentStore returns the underlying content Store
  519. func (c *Client) ContentStore() content.Store {
  520. if c.contentStore != nil {
  521. return c.contentStore
  522. }
  523. c.connMu.Lock()
  524. defer c.connMu.Unlock()
  525. return contentproxy.NewContentStore(contentapi.NewContentClient(c.conn))
  526. }
  527. // SnapshotService returns the underlying snapshotter for the provided snapshotter name
  528. func (c *Client) SnapshotService(snapshotterName string) snapshots.Snapshotter {
  529. snapshotterName, err := c.resolveSnapshotterName(context.Background(), snapshotterName)
  530. if err != nil {
  531. snapshotterName = DefaultSnapshotter
  532. }
  533. if c.snapshotters != nil {
  534. return c.snapshotters[snapshotterName]
  535. }
  536. c.connMu.Lock()
  537. defer c.connMu.Unlock()
  538. return snproxy.NewSnapshotter(snapshotsapi.NewSnapshotsClient(c.conn), snapshotterName)
  539. }
  540. // TaskService returns the underlying TasksClient
  541. func (c *Client) TaskService() tasks.TasksClient {
  542. if c.taskService != nil {
  543. return c.taskService
  544. }
  545. c.connMu.Lock()
  546. defer c.connMu.Unlock()
  547. return tasks.NewTasksClient(c.conn)
  548. }
  549. // ImageService returns the underlying image Store
  550. func (c *Client) ImageService() images.Store {
  551. if c.imageStore != nil {
  552. return c.imageStore
  553. }
  554. c.connMu.Lock()
  555. defer c.connMu.Unlock()
  556. return NewImageStoreFromClient(imagesapi.NewImagesClient(c.conn))
  557. }
  558. // DiffService returns the underlying Differ
  559. func (c *Client) DiffService() DiffService {
  560. if c.diffService != nil {
  561. return c.diffService
  562. }
  563. c.connMu.Lock()
  564. defer c.connMu.Unlock()
  565. return NewDiffServiceFromClient(diffapi.NewDiffClient(c.conn))
  566. }
  567. // IntrospectionService returns the underlying Introspection Client
  568. func (c *Client) IntrospectionService() introspection.Service {
  569. if c.introspectionService != nil {
  570. return c.introspectionService
  571. }
  572. c.connMu.Lock()
  573. defer c.connMu.Unlock()
  574. return introspection.NewIntrospectionServiceFromClient(introspectionapi.NewIntrospectionClient(c.conn))
  575. }
  576. // LeasesService returns the underlying Leases Client
  577. func (c *Client) LeasesService() leases.Manager {
  578. if c.leasesService != nil {
  579. return c.leasesService
  580. }
  581. c.connMu.Lock()
  582. defer c.connMu.Unlock()
  583. return leasesproxy.NewLeaseManager(leasesapi.NewLeasesClient(c.conn))
  584. }
  585. // HealthService returns the underlying GRPC HealthClient
  586. func (c *Client) HealthService() grpc_health_v1.HealthClient {
  587. c.connMu.Lock()
  588. defer c.connMu.Unlock()
  589. return grpc_health_v1.NewHealthClient(c.conn)
  590. }
  591. // EventService returns the underlying event service
  592. func (c *Client) EventService() EventService {
  593. if c.eventService != nil {
  594. return c.eventService
  595. }
  596. c.connMu.Lock()
  597. defer c.connMu.Unlock()
  598. return NewEventServiceFromClient(eventsapi.NewEventsClient(c.conn))
  599. }
  600. // VersionService returns the underlying VersionClient
  601. func (c *Client) VersionService() versionservice.VersionClient {
  602. c.connMu.Lock()
  603. defer c.connMu.Unlock()
  604. return versionservice.NewVersionClient(c.conn)
  605. }
  606. // Conn returns the underlying GRPC connection object
  607. func (c *Client) Conn() *grpc.ClientConn {
  608. c.connMu.Lock()
  609. defer c.connMu.Unlock()
  610. return c.conn
  611. }
  612. // Version of containerd
  613. type Version struct {
  614. // Version number
  615. Version string
  616. // Revision from git that was built
  617. Revision string
  618. }
  619. // Version returns the version of containerd that the client is connected to
  620. func (c *Client) Version(ctx context.Context) (Version, error) {
  621. c.connMu.Lock()
  622. if c.conn == nil {
  623. c.connMu.Unlock()
  624. return Version{}, errors.Wrap(errdefs.ErrUnavailable, "no grpc connection available")
  625. }
  626. c.connMu.Unlock()
  627. response, err := c.VersionService().Version(ctx, &ptypes.Empty{})
  628. if err != nil {
  629. return Version{}, err
  630. }
  631. return Version{
  632. Version: response.Version,
  633. Revision: response.Revision,
  634. }, nil
  635. }
  636. type ServerInfo struct {
  637. UUID string
  638. }
  639. func (c *Client) Server(ctx context.Context) (ServerInfo, error) {
  640. c.connMu.Lock()
  641. if c.conn == nil {
  642. c.connMu.Unlock()
  643. return ServerInfo{}, errors.Wrap(errdefs.ErrUnavailable, "no grpc connection available")
  644. }
  645. c.connMu.Unlock()
  646. response, err := c.IntrospectionService().Server(ctx, &ptypes.Empty{})
  647. if err != nil {
  648. return ServerInfo{}, err
  649. }
  650. return ServerInfo{
  651. UUID: response.UUID,
  652. }, nil
  653. }
  654. func (c *Client) resolveSnapshotterName(ctx context.Context, name string) (string, error) {
  655. if name == "" {
  656. label, err := c.GetLabel(ctx, defaults.DefaultSnapshotterNSLabel)
  657. if err != nil {
  658. return "", err
  659. }
  660. if label != "" {
  661. name = label
  662. } else {
  663. name = DefaultSnapshotter
  664. }
  665. }
  666. return name, nil
  667. }
  668. func (c *Client) getSnapshotter(ctx context.Context, name string) (snapshots.Snapshotter, error) {
  669. name, err := c.resolveSnapshotterName(ctx, name)
  670. if err != nil {
  671. return nil, err
  672. }
  673. s := c.SnapshotService(name)
  674. if s == nil {
  675. return nil, errors.Wrapf(errdefs.ErrNotFound, "snapshotter %s was not found", name)
  676. }
  677. return s, nil
  678. }
  679. // CheckRuntime returns true if the current runtime matches the expected
  680. // runtime. Providing various parts of the runtime schema will match those
  681. // parts of the expected runtime
  682. func CheckRuntime(current, expected string) bool {
  683. cp := strings.Split(current, ".")
  684. l := len(cp)
  685. for i, p := range strings.Split(expected, ".") {
  686. if i > l {
  687. return false
  688. }
  689. if p != cp[i] {
  690. return false
  691. }
  692. }
  693. return true
  694. }