client.go 24 KB

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