├── .gitignore ├── reformat.sh ├── .bazelrc ├── .clang-format ├── k8s.io └── kubernetes │ ├── pkg │ └── kubelet │ │ └── apis │ │ └── cri │ │ └── v1alpha1 │ │ └── runtime │ │ ├── BUILD.bazel │ │ └── api.proto │ └── LICENSE ├── scuba ├── util │ ├── BUILD.bazel │ ├── fd_streambuf.h │ └── grpc_connection_injector.h ├── runtime_service │ ├── iso8601_timestamp.h │ ├── iso8601_timestamp.cc │ ├── naming_scheme.h │ ├── BUILD.bazel │ ├── configuration.proto │ ├── naming_scheme.cc │ ├── ip_address_allocator.h │ ├── yaml_file_descriptor_factory.h │ ├── container.h │ ├── pod_sandbox.h │ ├── ip_address_allocator.cc │ ├── program_main.cc │ ├── yaml_file_descriptor_factory.cc │ ├── runtime_service.h │ ├── pod_sandbox.cc │ ├── container.cc │ └── runtime_service.cc └── image_service │ ├── BUILD.bazel │ ├── configuration.proto │ ├── image_service.h │ ├── program_main.cc │ └── image_service.cc ├── README.md ├── LICENSE └── WORKSPACE /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | /bazel-* 3 | -------------------------------------------------------------------------------- /reformat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec find scuba \( -name '*.cc' -o -name '*.h' \) -exec clang-format -i {} + 3 | -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | build --cpu=x86_64-unknown-cloudabi 2 | build --crosstool_top=@org_cloudabi_bazel_toolchains_cloudabi//:toolchain 3 | build --python_path=python3 4 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AllowShortIfStatementsOnASingleLine: false 3 | AllowShortLoopsOnASingleLine: false 4 | AllowShortFunctionsOnASingleLine: None 5 | DerivePointerBinding: false 6 | -------------------------------------------------------------------------------- /k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") 2 | 3 | cc_grpc_library( 4 | name = "api_proto", 5 | srcs = ["api.proto"], 6 | proto_only = False, 7 | visibility = ["//visibility:public"], 8 | well_known_protos = False, 9 | deps = [], 10 | ) 11 | -------------------------------------------------------------------------------- /scuba/util/BUILD.bazel: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "fd_streambuf", 3 | hdrs = ["fd_streambuf.h"], 4 | visibility = ["//visibility:public"], 5 | ) 6 | 7 | cc_library( 8 | name = "grpc_connection_injector", 9 | hdrs = ["grpc_connection_injector.h"], 10 | visibility = ["//visibility:public"], 11 | deps = [ 12 | "@com_github_grpc_grpc//:grpc++", 13 | "@org_cloudabi_arpc//:arpc", 14 | "@org_cloudabi_flower//:flower_protocol", 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /scuba/runtime_service/iso8601_timestamp.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_RUNTIME_SERVICE_ISO8601_TIMESTAMP_H 6 | #define SCUBA_RUNTIME_SERVICE_ISO8601_TIMESTAMP_H 7 | 8 | #include 9 | #include 10 | 11 | namespace scuba { 12 | namespace runtime_service { 13 | 14 | class ISO8601Timestamp { 15 | public: 16 | ISO8601Timestamp(); 17 | 18 | friend std::ostream& operator<<(std::ostream& stream, 19 | const ISO8601Timestamp& time); 20 | 21 | private: 22 | std::tm tm_; 23 | long tv_nsec_; 24 | }; 25 | 26 | std::ostream& operator<<(std::ostream& stream, const ISO8601Timestamp& time); 27 | 28 | } // namespace runtime_service 29 | } // namespace scuba 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /scuba/image_service/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@org_cloudabi_arpc//:aprotoc.bzl", "aprotoc") 2 | load("@org_cloudabi_bazel_toolchains_cloudabi//:cc.bzl", "cc_binary_cloudabi") 3 | 4 | cc_binary_cloudabi( 5 | name = "scuba_image_service", 6 | srcs = [ 7 | "configuration.ad.h", 8 | "image_service.cc", 9 | "image_service.h", 10 | "program_main.cc", 11 | ], 12 | deps = [ 13 | "//k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime:api_proto", 14 | "//scuba/util:grpc_connection_injector", 15 | "@com_github_grpc_grpc//:grpc++", 16 | "@org_cloudabi_arpc//:arpc", 17 | "@org_cloudabi_flower//:flower_protocol", 18 | ], 19 | ) 20 | 21 | aprotoc( 22 | name = "scuba_image_service_configuration", 23 | src = "configuration.proto", 24 | ) 25 | -------------------------------------------------------------------------------- /scuba/runtime_service/iso8601_timestamp.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include "scuba/runtime_service/iso8601_timestamp.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | using scuba::runtime_service::ISO8601Timestamp; 12 | 13 | ISO8601Timestamp::ISO8601Timestamp() { 14 | timespec ts; 15 | clock_gettime(CLOCK_REALTIME, &ts); 16 | localtime_r(&ts.tv_sec, &tm_); 17 | tv_nsec_ = ts.tv_nsec; 18 | } 19 | 20 | namespace scuba { 21 | namespace runtime_service { 22 | 23 | std::ostream& operator<<(std::ostream& stream, const ISO8601Timestamp& time) { 24 | stream << std::put_time(&time.tm_, "%FT%T") << '.' << std::setfill('0') 25 | << std::setw(9) << time.tv_nsec_ << 'Z'; 26 | return stream; 27 | } 28 | 29 | } // namespace runtime_service 30 | } // namespace scuba 31 | -------------------------------------------------------------------------------- /scuba/runtime_service/naming_scheme.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_RUNTIME_SERVICE_NAMING_SCHEME_H 6 | #define SCUBA_RUNTIME_SERVICE_NAMING_SCHEME_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.pb.h" 13 | 14 | namespace scuba { 15 | namespace runtime_service { 16 | 17 | class NamingScheme { 18 | public: 19 | static std::string CreatePodSandboxName( 20 | const runtime::PodSandboxMetadata& metadata); 21 | static std::string CreateContainerName( 22 | const runtime::ContainerMetadata& metadata); 23 | 24 | static std::string ComposePodSandboxContainerName( 25 | std::string_view pod_sandbox_id, std::string_view container_id); 26 | static std::pair 27 | DecomposePodSandboxContainerName(const std::string_view id); 28 | }; 29 | 30 | } // namespace runtime_service 31 | } // namespace scuba 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /scuba/util/fd_streambuf.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_UTIL_FD_STREAMBUF_H 6 | #define SCUBA_UTIL_FD_STREAMBUF_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "arpc++/arpc++.h" 13 | 14 | namespace scuba { 15 | namespace util { 16 | 17 | // Stream buffer that writes data to a file descriptor. 18 | // TODO(ed): Add buffering. 19 | class fd_streambuf : public std::streambuf { 20 | public: 21 | explicit fd_streambuf(const std::shared_ptr& fd) 22 | : fd_(fd) { 23 | } 24 | 25 | int overflow(int c) override { 26 | char ch = c; 27 | if (write(fd_->get(), &ch, 1) != 1) 28 | return traits_type::eof(); 29 | return traits_type::to_int_type(c); 30 | } 31 | 32 | std::streamsize xsputn(const char* s, std::streamsize n) override { 33 | return write(fd_->get(), s, n); 34 | } 35 | 36 | private: 37 | std::shared_ptr fd_; 38 | }; 39 | 40 | } // namespace util 41 | } // namespace scuba 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scuba: A Kubernetes CRI for launching CloudABI processes 2 | 3 | [Kubernetes](https://kubernetes.io/) is an Open Source cluster 4 | management suite. By default, it can be used to distributed Docker-based 5 | containers across a cluster of Linux servers. As of version 1.5, 6 | Kubernetes has turned its coupling with Docker into a separate protocol, 7 | called the Container Runtime Interface (CRI). This allows Kubernetes to 8 | interface with other container engines, such as rkt, OCI, etc. 9 | 10 | Scuba (contraction of 'Secure Kubernetes') is a daemon that implements 11 | the Container Runtime Interface, using [GRPC](https://grpc.io/). Instead 12 | of spawning containers, it simply launches 13 | [CloudABI](https://nuxi.nl/cloudabi/) processes. This allows you to 14 | spawn one or more nodes in a Kubernetes cluster, capable of running 15 | strongly sandboxed applications. These jobs don't depend on network 16 | policies, password-based authentication or key exchange to be secure. 17 | Instead, pods are simply able to connect to services if they are 18 | configured through their pod specification to do so. 19 | 20 | # Using Scuba 21 | 22 | Scuba is still under heavy development, so documentation on using it is 23 | still absent. Stay tuned for updates in the nearby future! 24 | -------------------------------------------------------------------------------- /scuba/runtime_service/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@org_cloudabi_arpc//:aprotoc.bzl", "aprotoc") 2 | load("@org_cloudabi_bazel_toolchains_cloudabi//:cc.bzl", "cc_binary_cloudabi") 3 | 4 | cc_binary_cloudabi( 5 | name = "scuba_runtime_service", 6 | srcs = [ 7 | "configuration.ad.h", 8 | "container.cc", 9 | "container.h", 10 | "ip_address_allocator.cc", 11 | "ip_address_allocator.h", 12 | "iso8601_timestamp.cc", 13 | "iso8601_timestamp.h", 14 | "naming_scheme.cc", 15 | "naming_scheme.h", 16 | "pod_sandbox.cc", 17 | "pod_sandbox.h", 18 | "program_main.cc", 19 | "runtime_service.cc", 20 | "runtime_service.h", 21 | "yaml_file_descriptor_factory.cc", 22 | "yaml_file_descriptor_factory.h", 23 | ], 24 | deps = [ 25 | "//k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime:api_proto", 26 | "//scuba/util:fd_streambuf", 27 | "//scuba/util:grpc_connection_injector", 28 | "@com_github_grpc_grpc//:grpc++", 29 | "@com_github_jbeder_yaml_cpp//:yaml_cpp", 30 | "@org_cloudabi_arpc//:arpc", 31 | "@org_cloudabi_flower//:flower_protocol", 32 | "@org_cloudabi_yaml2argdata//:yaml2argdata", 33 | ], 34 | ) 35 | 36 | aprotoc( 37 | name = "scuba_runtime_service_configuration", 38 | src = "configuration.proto", 39 | ) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | All code in the scuba/ directory is distributed under the following license: 2 | 3 | Copyright (c) 2017 Nuxi, https://nuxi.nl/ 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /scuba/util/grpc_connection_injector.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_UTIL_GRPC_CONNECTION_INJECTOR_H 6 | #define SCUBA_UTIL_GRPC_CONNECTION_INJECTOR_H 7 | 8 | #include 9 | 10 | #include "arpc++/arpc++.h" 11 | #include "flower/protocol/server.ad.h" 12 | #include "grpc++/grpc++.h" 13 | 14 | namespace scuba { 15 | namespace util { 16 | 17 | // Bridge for accepting incoming connections from Flower and passing 18 | // them on to an existing GRPC server instance. 19 | class GrpcConnectionInjector 20 | : public flower::protocol::server::Server::Service { 21 | public: 22 | explicit GrpcConnectionInjector(grpc::Server* server) : server_(server) { 23 | } 24 | 25 | arpc::Status Connect(arpc::ServerContext* context, 26 | const flower::protocol::server::ConnectRequest* request, 27 | flower::protocol::server::ConnectResponse* response) { 28 | if (const std::shared_ptr& fd = request->client(); 29 | fd) { 30 | // Duplicate the file descriptor, as GRPC wants to take ownership. 31 | // The file descriptor provided by ARPC cannot be taken over. 32 | if (int nfd = dup(fd->get()); nfd >= 0) { 33 | fcntl(nfd, F_SETFL, fcntl(nfd, F_GETFL) | O_NONBLOCK); 34 | grpc::AddInsecureChannelFromFd(server_, nfd); 35 | } 36 | } 37 | return arpc::Status::OK; 38 | } 39 | 40 | private: 41 | grpc::Server* server_; 42 | }; 43 | 44 | } // namespace util 45 | } // namespace scuba 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /scuba/image_service/configuration.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 1. Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // 12 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | // SUCH DAMAGE. 23 | 24 | syntax = 'proto3'; 25 | 26 | package scuba.image_service; 27 | 28 | message Configuration { 29 | fd cri_switchboard_handle = 1; 30 | fd image_directory = 2; 31 | fd logger_output = 3; 32 | } 33 | -------------------------------------------------------------------------------- /scuba/runtime_service/configuration.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 1. Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // 12 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | // SUCH DAMAGE. 23 | 24 | syntax = 'proto3'; 25 | 26 | package scuba.runtime_service; 27 | 28 | message Configuration { 29 | fd cri_switchboard_handle = 1; 30 | fd image_directory = 2; 31 | fd root_directory = 3; 32 | fd containers_switchboard_handle = 4; 33 | fd logger_output = 5; 34 | } 35 | -------------------------------------------------------------------------------- /scuba/runtime_service/naming_scheme.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include "scuba/runtime_service/naming_scheme.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.pb.h" 14 | 15 | using runtime::ContainerMetadata; 16 | using runtime::PodSandboxMetadata; 17 | using scuba::runtime_service::NamingScheme; 18 | 19 | std::string NamingScheme::CreatePodSandboxName( 20 | const PodSandboxMetadata& metadata) { 21 | std::ostringstream ss; 22 | ss << "name=" << metadata.name() << ",uid=" << metadata.uid() 23 | << ",namespace=" << metadata.namespace_() 24 | << ",attempt=" << metadata.attempt(); 25 | return ss.str(); 26 | } 27 | 28 | std::string NamingScheme::CreateContainerName( 29 | const ContainerMetadata& metadata) { 30 | std::ostringstream ss; 31 | ss << "name=" << metadata.name() << ",attempt=" << metadata.attempt(); 32 | return ss.str(); 33 | } 34 | 35 | std::string NamingScheme::ComposePodSandboxContainerName( 36 | std::string_view pod_sandbox_id, std::string_view container_id) { 37 | std::ostringstream ss; 38 | ss << pod_sandbox_id << "|" << container_id; 39 | return ss.str(); 40 | } 41 | 42 | std::pair 43 | NamingScheme::DecomposePodSandboxContainerName(std::string_view id) { 44 | auto split = std::find(id.begin(), id.end(), '|'); 45 | if (split == id.end()) 46 | return {}; 47 | return {std::string_view(id.begin(), split - id.begin()), 48 | std::string_view(split + 1, id.end() - (split + 1))}; 49 | } 50 | -------------------------------------------------------------------------------- /scuba/runtime_service/ip_address_allocator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_RUNTIME_SERVICE_IP_ADDRESS_ALLOCATOR_H 6 | #define SCUBA_RUNTIME_SERVICE_IP_ADDRESS_ALLOCATOR_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace scuba { 14 | namespace runtime_service { 15 | 16 | class IPAddressAllocator; 17 | 18 | class IPAddressLease { 19 | public: 20 | IPAddressLease() : allocator_(nullptr) { 21 | } 22 | 23 | IPAddressLease(IPAddressAllocator* allocator, std::uint32_t address) 24 | : allocator_(allocator), address_(address) { 25 | } 26 | 27 | IPAddressLease(IPAddressLease&& lease); 28 | IPAddressLease& operator=(IPAddressLease&& lease); 29 | ~IPAddressLease(); 30 | 31 | std::string GetString() const; 32 | 33 | private: 34 | IPAddressAllocator* allocator_; 35 | std::uint32_t address_; 36 | 37 | IPAddressLease(const IPAddressLease&) = delete; 38 | IPAddressLease& operator=(const IPAddressLease&) = delete; 39 | }; 40 | 41 | class IPAddressAllocator { 42 | public: 43 | IPAddressAllocator() : first_(1), last_(0) { 44 | } 45 | 46 | bool SetRange(std::string_view range); 47 | IPAddressLease Allocate(); 48 | void Deallocate(std::uint32_t address); 49 | 50 | private: 51 | std::uint32_t first_; // First allocatable address. 52 | std::uint32_t last_; // Last allocatable address. 53 | 54 | std::random_device random_device_; // Random address picker. 55 | std::set used_; // Addresses currently in use. 56 | 57 | IPAddressAllocator(IPAddressAllocator&) = delete; 58 | void operator=(IPAddressAllocator) = delete; 59 | }; 60 | 61 | } // namespace runtime_service 62 | } // namespace scuba 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /scuba/image_service/image_service.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_IMAGE_SERVICE_IMAGE_SERVICE_H 6 | #define SCUBA_IMAGE_SERVICE_IMAGE_SERVICE_H 7 | 8 | #include 9 | 10 | #include "arpc++/arpc++.h" 11 | #include "grpc++/grpc++.h" 12 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.grpc.pb.h" 13 | 14 | namespace scuba { 15 | namespace image_service { 16 | 17 | class ImageService final : public runtime::ImageService::Service { 18 | public: 19 | ImageService(const arpc::FileDescriptor* image_directory) 20 | : image_directory_(image_directory) { 21 | } 22 | 23 | grpc::Status ListImages(grpc::ServerContext* context, 24 | const runtime::ListImagesRequest* request, 25 | runtime::ListImagesResponse* response) override; 26 | grpc::Status ImageStatus(grpc::ServerContext* context, 27 | const runtime::ImageStatusRequest* request, 28 | runtime::ImageStatusResponse* response) override; 29 | grpc::Status PullImage(grpc::ServerContext* context, 30 | const runtime::PullImageRequest* request, 31 | runtime::PullImageResponse* response) override; 32 | grpc::Status RemoveImage(grpc::ServerContext* context, 33 | const runtime::RemoveImageRequest* request, 34 | runtime::RemoveImageResponse* response) override; 35 | grpc::Status ImageFsInfo(grpc::ServerContext* context, 36 | const runtime::ImageFsInfoRequest* request, 37 | runtime::ImageFsInfoResponse* response) override; 38 | 39 | private: 40 | const arpc::FileDescriptor* const image_directory_; 41 | 42 | // Returns whether the name of an image corresponds with a locally 43 | // stored image, i.e., it is named "sha256:....". 44 | static bool IsLocalImageName_(std::string_view image_name); 45 | 46 | ImageService(ImageService&) = delete; 47 | void operator=(ImageService) = delete; 48 | }; 49 | 50 | } // namespace image_service 51 | } // namespace scuba 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "org_cloudabi_scuba") 2 | 3 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") 4 | 5 | git_repository( 6 | name = "org_cloudabi_arpc", 7 | commit = "81305e311c0559fe7a64a98ce8ac1aa7051c7a4d", 8 | remote = "https://github.com/NuxiNL/arpc.git", 9 | ) 10 | 11 | git_repository( 12 | name = "org_cloudabi_bazel_toolchains_cloudabi", 13 | commit = "4eb362f2e4d8a5e29ac4b6638ef9d4b9fd312b32", 14 | remote = "https://github.com/NuxiNL/bazel-toolchains-cloudabi.git", 15 | ) 16 | 17 | git_repository( 18 | name = "org_cloudabi_bazel_third_party", 19 | commit = "8d51abda2299d5fe26ca7c55f182b6562f440979", 20 | remote = "https://github.com/NuxiNL/bazel-third-party.git", 21 | ) 22 | 23 | git_repository( 24 | name = "org_cloudabi_flower", 25 | commit = "f23a1bfc9a1d3f16920b9183b967a057d8a21183", 26 | remote = "https://github.com/NuxiNL/flower.git", 27 | ) 28 | 29 | git_repository( 30 | name = "org_cloudabi_yaml2argdata", 31 | commit = "678be75c7a9bb23c80a8de4bbbccf26bba9570aa", 32 | remote = "https://github.com/NuxiNL/yaml2argdata.git", 33 | ) 34 | 35 | git_repository( 36 | name = "io_bazel_rules_python", 37 | commit = "e6399b601e2f72f74e5aa635993d69166784dde1", 38 | remote = "https://github.com/bazelbuild/rules_python.git", 39 | ) 40 | 41 | load("@org_cloudabi_bazel_toolchains_cloudabi//:toolchains.bzl", "toolchains_cloudabi_dependencies") 42 | 43 | toolchains_cloudabi_dependencies() 44 | 45 | load("@org_cloudabi_bazel_third_party//:third_party.bzl", "third_party_repositories") 46 | 47 | third_party_repositories() 48 | 49 | load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") 50 | 51 | grpc_deps() 52 | 53 | # TODO(ed): Remove the dependencies below once Bazel supports transitive 54 | # dependency loading. These are from ARPC. 55 | # https://github.com/bazelbuild/proposals/blob/master/designs/2018-11-07-design-recursive-workspaces.md 56 | 57 | load("@io_bazel_rules_python//python:pip.bzl", "pip_import", "pip_repositories") 58 | 59 | pip_repositories() 60 | 61 | pip_import( 62 | name = "aprotoc_deps", 63 | requirements = "@org_cloudabi_arpc//scripts:requirements.txt", 64 | ) 65 | 66 | load("@aprotoc_deps//:requirements.bzl", "pip_install") 67 | 68 | pip_install() 69 | -------------------------------------------------------------------------------- /scuba/runtime_service/yaml_file_descriptor_factory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_RUNTIME_SERVICE_YAML_FILE_DESCRIPTOR_FACTORY_H 6 | #define SCUBA_RUNTIME_SERVICE_YAML_FILE_DESCRIPTOR_FACTORY_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "argdata.hpp" 16 | #include "flower/protocol/switchboard.ad.h" 17 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.pb.h" 18 | #include "yaml-cpp/mark.h" 19 | #include "yaml2argdata/yaml_factory.h" 20 | 21 | namespace scuba { 22 | namespace runtime_service { 23 | 24 | class YAMLFileDescriptorFactory 25 | : public yaml2argdata::YAMLFactory { 26 | public: 27 | YAMLFileDescriptorFactory( 28 | const runtime::PodSandboxMetadata* pod_metadata, 29 | const runtime::ContainerMetadata* container_metadata, 30 | const arpc::FileDescriptor* container_log, 31 | const std::map>* mounts, 32 | flower::protocol::switchboard::Switchboard::Stub* switchboard_servers, 33 | YAMLFactory* fallback) 34 | : pod_metadata_(pod_metadata), 35 | container_metadata_(container_metadata), 36 | container_log_(container_log), 37 | mounts_(mounts), 38 | switchboard_servers_(switchboard_servers), 39 | fallback_(fallback) { 40 | } 41 | 42 | const argdata_t* GetNull(const YAML::Mark& mark) override; 43 | const argdata_t* GetScalar(const YAML::Mark& mark, std::string_view tag, 44 | std::string_view value) override; 45 | const argdata_t* GetSequence(const YAML::Mark& mark, std::string_view tag, 46 | std::vector elements) override; 47 | const argdata_t* GetMap(const YAML::Mark& mark, std::string_view tag, 48 | std::vector keys, 49 | std::vector values) override; 50 | 51 | private: 52 | const runtime::PodSandboxMetadata* const pod_metadata_; 53 | const runtime::ContainerMetadata* const container_metadata_; 54 | const arpc::FileDescriptor* const container_log_; 55 | const std::map>* const mounts_; 56 | flower::protocol::switchboard::Switchboard::Stub* const switchboard_servers_; 57 | YAMLFactory* const fallback_; 58 | 59 | std::vector> argdatas_; 60 | std::vector> fds_; 61 | }; 62 | 63 | } // namespace runtime_service 64 | } // namespace scuba 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /scuba/runtime_service/container.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_RUNTIME_SERVICE_CONTAINER_H 6 | #define SCUBA_RUNTIME_SERVICE_CONTAINER_H 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "arpc++/arpc++.h" 18 | #include "flower/protocol/switchboard.ad.h" 19 | #include "google/protobuf/map.h" 20 | #include "google/protobuf/repeated_field.h" 21 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.pb.h" 22 | 23 | namespace scuba { 24 | namespace runtime_service { 25 | 26 | class IPAddressLease; 27 | 28 | class Container { 29 | public: 30 | explicit Container(const runtime::ContainerConfig& config); 31 | ~Container(); 32 | 33 | void GetInfo(runtime::Container* info); 34 | void GetStatus(runtime::ContainerStatus* status); 35 | 36 | bool MatchesFilter( 37 | std::optional state, 38 | const google::protobuf::Map& labels); 39 | 40 | void Start(const runtime::PodSandboxMetadata& pod_metadata, 41 | const arpc::FileDescriptor& root_directory, 42 | const arpc::FileDescriptor& image_directory, 43 | const arpc::FileDescriptor& log_directory, 44 | flower::protocol::switchboard::Switchboard::Stub* 45 | containers_switchboard_handle); 46 | void Stop(std::int64_t timeout); 47 | 48 | Container(Container&) = delete; 49 | void operator=(Container) = delete; 50 | 51 | private: 52 | std::unique_ptr OpenContainerLog_( 53 | const arpc::FileDescriptor& log_directory); 54 | 55 | // Data that should be returned through ContainerStatus. 56 | const runtime::ContainerMetadata metadata_; 57 | const runtime::ImageSpec image_; 58 | const std::chrono::system_clock::time_point creation_time_; 59 | const google::protobuf::Map labels_; 60 | const google::protobuf::Map annotations_; 61 | const google::protobuf::RepeatedPtrField mounts_; 62 | const std::string log_path_; 63 | const std::string argdata_; 64 | 65 | // Event loop that is used for managing subprocess lifetime. 66 | static std::mutex child_loop_lock_; 67 | static uv_loop_t child_loop_; 68 | 69 | // Fields modified by event loop callbacks, guarded by the loop's lock. 70 | uv_process_t child_process_; 71 | runtime::ContainerState container_state_; 72 | std::chrono::system_clock::time_point start_time_; 73 | std::chrono::system_clock::time_point finish_time_; 74 | std::int32_t exit_code_; 75 | }; 76 | 77 | } // namespace runtime_service 78 | } // namespace scuba 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /scuba/runtime_service/pod_sandbox.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_RUNTIME_SERVICE_POD_SANDBOX_H 6 | #define SCUBA_RUNTIME_SERVICE_POD_SANDBOX_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "arpc++/arpc++.h" 20 | #include "flower/protocol/switchboard.ad.h" 21 | #include "google/protobuf/map.h" 22 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.pb.h" 23 | #include "scuba/runtime_service/container.h" 24 | #include "scuba/runtime_service/ip_address_allocator.h" 25 | 26 | namespace scuba { 27 | namespace runtime_service { 28 | 29 | class PodSandbox { 30 | public: 31 | explicit PodSandbox(const runtime::PodSandboxConfig& config, 32 | IPAddressLease ip); 33 | 34 | void GetInfo(runtime::PodSandbox* info); 35 | void GetStatus(runtime::PodSandboxStatus* status); 36 | void Stop(); 37 | 38 | bool MatchesFilter( 39 | std::optional state, 40 | const google::protobuf::Map& labels); 41 | 42 | void CreateContainer(std::string_view container_id, 43 | const runtime::ContainerConfig& config); 44 | void RemoveContainer(std::string_view container_id); 45 | void StartContainer(std::string_view container_id, 46 | const arpc::FileDescriptor& root_directory, 47 | const arpc::FileDescriptor& image_directory, 48 | flower::protocol::switchboard::Switchboard::Stub* 49 | containers_switchboard_handle); 50 | bool StopContainer(std::string_view container_id, std::int64_t timeout); 51 | std::vector> GetContainerInfo( 52 | std::string_view container_id, 53 | std::optional state, 54 | const google::protobuf::Map& labels); 55 | bool GetContainerStatus(std::string_view container_id, 56 | runtime::ContainerStatus* status); 57 | 58 | private: 59 | // Data that should be returned through PodSandboxStatus. 60 | const runtime::PodSandboxMetadata metadata_; 61 | const std::string log_directory_; 62 | const std::chrono::system_clock::time_point creation_time_; 63 | const google::protobuf::Map labels_; 64 | const google::protobuf::Map annotations_; 65 | const IPAddressLease ip_address_lease_; 66 | 67 | std::shared_mutex lock_; 68 | runtime::PodSandboxState state_; 69 | std::map, std::less<>> containers_; 70 | 71 | PodSandbox(PodSandbox&) = delete; 72 | void operator=(PodSandbox) = delete; 73 | }; 74 | 75 | } // namespace runtime_service 76 | } // namespace scuba 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /scuba/image_service/program_main.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "arpc++/arpc++.h" 11 | #include "flower/protocol/switchboard.ad.h" 12 | #include "grpc++/grpc++.h" 13 | #include "scuba/image_service/configuration.ad.h" 14 | #include "scuba/image_service/image_service.h" 15 | #include "scuba/util/grpc_connection_injector.h" 16 | 17 | using arpc::ArgdataParser; 18 | using arpc::ClientContext; 19 | using arpc::FileDescriptor; 20 | using arpc::Status; 21 | using flower::protocol::switchboard::ServerStartRequest; 22 | using flower::protocol::switchboard::ServerStartResponse; 23 | using flower::protocol::switchboard::Switchboard; 24 | using scuba::image_service::Configuration; 25 | using scuba::image_service::ImageService; 26 | using scuba::util::GrpcConnectionInjector; 27 | 28 | void program_main(const argdata_t* ad) { 29 | Configuration configuration; 30 | ArgdataParser argdata_parser; 31 | configuration.Parse(*ad, &argdata_parser); 32 | 33 | // Enable logging of failed assertions. 34 | if (const std::shared_ptr& logger_output = 35 | configuration.logger_output(); 36 | logger_output) { 37 | FILE* fp = fdopen(logger_output->get(), "w"); 38 | if (fp != nullptr) { 39 | setvbuf(fp, nullptr, _IONBF, 0); 40 | fswap(fp, stderr); 41 | fclose(fp); 42 | } 43 | } 44 | 45 | // Extract file descriptors. 46 | const std::shared_ptr& cri_switchboard_handle_fd = 47 | configuration.cri_switchboard_handle(); 48 | if (!cri_switchboard_handle_fd) 49 | std::exit(1); 50 | std::unique_ptr cri_switchboard_handle = 51 | Switchboard::NewStub(CreateChannel(cri_switchboard_handle_fd)); 52 | const std::shared_ptr& image_directory = 53 | configuration.image_directory(); 54 | if (!image_directory) 55 | std::exit(1); 56 | 57 | // Start the CRI service using GRPC. 58 | ImageService image_service(image_directory.get()); 59 | grpc::ServerBuilder cri_builder; 60 | cri_builder.RegisterService(&image_service); 61 | std::unique_ptr cri_server(cri_builder.BuildAndStart()); 62 | if (!cri_server) 63 | std::exit(1); 64 | 65 | // Listen for incoming connections for the CRI service. 66 | ClientContext context; 67 | ServerStartRequest request; 68 | ServerStartResponse response; 69 | if (Status status = 70 | cri_switchboard_handle->ServerStart(&context, request, &response); 71 | !status.ok()) 72 | std::exit(1); 73 | if (!response.server()) 74 | std::exit(1); 75 | 76 | // Forward incoming connections to GRPC. 77 | GrpcConnectionInjector injector(cri_server.get()); 78 | arpc::ServerBuilder injector_builder(response.server()); 79 | injector_builder.RegisterService(&injector); 80 | std::unique_ptr injector_server(injector_builder.Build()); 81 | while (injector_server->HandleRequest() == 0) { 82 | } 83 | std::exit(1); 84 | } 85 | -------------------------------------------------------------------------------- /scuba/runtime_service/ip_address_allocator.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include "scuba/runtime_service/ip_address_allocator.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using scuba::runtime_service::IPAddressAllocator; 17 | using scuba::runtime_service::IPAddressLease; 18 | 19 | IPAddressLease::IPAddressLease(IPAddressLease&& lease) { 20 | allocator_ = lease.allocator_; 21 | address_ = lease.address_; 22 | lease.allocator_ = nullptr; 23 | } 24 | 25 | IPAddressLease::~IPAddressLease() { 26 | if (allocator_ != nullptr) 27 | allocator_->Deallocate(address_); 28 | } 29 | 30 | IPAddressLease& IPAddressLease::operator=(IPAddressLease&& lease) { 31 | if (allocator_ != nullptr) 32 | allocator_->Deallocate(address_); 33 | allocator_ = lease.allocator_; 34 | address_ = lease.address_; 35 | lease.allocator_ = nullptr; 36 | return *this; 37 | } 38 | 39 | std::string IPAddressLease::GetString() const { 40 | assert(allocator_ != nullptr && "Attempted to access stale address"); 41 | std::ostringstream ss; 42 | ss << (address_ >> 24) << '.' << (address_ >> 16 & 0xff) << '.' 43 | << (address_ >> 8 & 0xff) << '.' << (address_ & 0xff); 44 | return ss.str(); 45 | } 46 | 47 | bool IPAddressAllocator::SetRange(std::string_view range) { 48 | // Parse an IPv4 address with prefix length. 49 | // TODO(ed): Support IPv6 as soon as Kubernetes does as well. 50 | // TODO(ed): Is there a way to avoid copying? 51 | std::string range_copy(range); 52 | std::istringstream ss(range_copy); 53 | std::uint32_t a, b, c, d; 54 | unsigned int prefixlen; 55 | char s1, s2, s3, s4; 56 | ss >> std::noskipws >> a >> s1 >> b >> s2 >> c >> s3 >> d >> s4 >> prefixlen; 57 | if (!ss.eof() || ss.fail() || a > 255 || s1 != '.' || b > 255 || s2 != '.' || 58 | c > 255 || s3 != '.' || d > 255 || s4 != '/' || prefixlen > 32) 59 | return false; 60 | 61 | if (prefixlen > 30) { 62 | // Prefix length of 31 or 32, meaning there are no network and 63 | // broadcast addresses. 64 | std::uint32_t mask = std::uint32_t(~0) << (32 - prefixlen); 65 | std::uint32_t base = (a << 24 | b << 16 | c << 8 | d) & mask; 66 | first_ = base; 67 | last_ = base | ~mask; 68 | } else { 69 | // Prefix length of 30 or less, meaning we should keep the network 70 | // and broadcast addresses free. 71 | std::uint32_t mask = ~(std::uint32_t(~0) >> prefixlen); 72 | std::uint32_t base = (a << 24 | b << 16 | c << 8 | d) & mask; 73 | first_ = base | 0x00000001; 74 | last_ = base | (0xfffffffe & ~mask); 75 | } 76 | return true; 77 | } 78 | 79 | IPAddressLease IPAddressAllocator::Allocate() { 80 | if (first_ > last_) 81 | throw std::runtime_error("No IP address range configured"); 82 | 83 | // First try to allocate an address at random. 84 | std::uint32_t address; 85 | std::uniform_int_distribution dist(first_, last_); 86 | for (int i = 0; i < 100; ++i) { 87 | address = dist(random_device_); 88 | if (used_.find(address) == used_.end()) { 89 | used_.insert(address); 90 | return IPAddressLease(this, address); 91 | } 92 | } 93 | 94 | // Fall back to doing a full sweep of the address range. 95 | for (address = first_; address >= first_ && address <= last_; ++address) { 96 | if (used_.find(address) == used_.end()) { 97 | used_.insert(address); 98 | return IPAddressLease(this, address); 99 | } 100 | } 101 | throw std::runtime_error("No unused IP addresses available"); 102 | } 103 | 104 | void IPAddressAllocator::Deallocate(std::uint32_t address) { 105 | used_.erase(address); 106 | } 107 | -------------------------------------------------------------------------------- /scuba/runtime_service/program_main.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "arpc++/arpc++.h" 11 | #include "flower/protocol/switchboard.ad.h" 12 | #include "grpc++/grpc++.h" 13 | #include "scuba/runtime_service/configuration.ad.h" 14 | #include "scuba/runtime_service/ip_address_allocator.h" 15 | #include "scuba/runtime_service/runtime_service.h" 16 | #include "scuba/util/grpc_connection_injector.h" 17 | 18 | using arpc::ArgdataParser; 19 | using arpc::ClientContext; 20 | using arpc::FileDescriptor; 21 | using arpc::Status; 22 | using flower::protocol::switchboard::ServerStartRequest; 23 | using flower::protocol::switchboard::ServerStartResponse; 24 | using flower::protocol::switchboard::Switchboard; 25 | using scuba::runtime_service::Configuration; 26 | using scuba::runtime_service::IPAddressAllocator; 27 | using scuba::runtime_service::RuntimeService; 28 | using scuba::util::GrpcConnectionInjector; 29 | 30 | void program_main(const argdata_t* ad) { 31 | Configuration configuration; 32 | ArgdataParser argdata_parser; 33 | configuration.Parse(*ad, &argdata_parser); 34 | 35 | // Enable logging of failed assertions. 36 | if (const std::shared_ptr& logger_output = 37 | configuration.logger_output(); 38 | logger_output) { 39 | FILE* fp = fdopen(logger_output->get(), "w"); 40 | if (fp != nullptr) { 41 | setvbuf(fp, nullptr, _IONBF, 0); 42 | fswap(fp, stderr); 43 | fclose(fp); 44 | } 45 | } 46 | 47 | // Extract file descriptors. 48 | const std::shared_ptr& cri_switchboard_handle_fd = 49 | configuration.cri_switchboard_handle(); 50 | if (!cri_switchboard_handle_fd) 51 | std::exit(1); 52 | std::unique_ptr cri_switchboard_handle = 53 | Switchboard::NewStub(CreateChannel(cri_switchboard_handle_fd)); 54 | const std::shared_ptr& image_directory = 55 | configuration.image_directory(); 56 | if (!image_directory) 57 | std::exit(1); 58 | const std::shared_ptr& root_directory = 59 | configuration.root_directory(); 60 | if (!root_directory) 61 | std::exit(1); 62 | const std::shared_ptr& containers_switchboard_handle_fd = 63 | configuration.containers_switchboard_handle(); 64 | if (!containers_switchboard_handle_fd) 65 | std::exit(1); 66 | std::unique_ptr containers_switchboard_handle = 67 | Switchboard::NewStub(CreateChannel(containers_switchboard_handle_fd)); 68 | 69 | // Start the CRI service using GRPC. 70 | IPAddressAllocator ip_address_allocator; 71 | RuntimeService runtime_service(root_directory.get(), image_directory.get(), 72 | containers_switchboard_handle.get(), 73 | &ip_address_allocator); 74 | grpc::ServerBuilder cri_builder; 75 | cri_builder.RegisterService(&runtime_service); 76 | std::unique_ptr cri_server(cri_builder.BuildAndStart()); 77 | if (!cri_server) 78 | std::exit(1); 79 | 80 | // Listen for incoming connections for the CRI service. 81 | ClientContext context; 82 | ServerStartRequest request; 83 | ServerStartResponse response; 84 | if (Status status = 85 | cri_switchboard_handle->ServerStart(&context, request, &response); 86 | !status.ok()) 87 | std::exit(1); 88 | if (!response.server()) 89 | std::exit(1); 90 | 91 | // Forward incoming connections to GRPC. 92 | GrpcConnectionInjector injector(cri_server.get()); 93 | arpc::ServerBuilder injector_builder(response.server()); 94 | injector_builder.RegisterService(&injector); 95 | std::unique_ptr injector_server(injector_builder.Build()); 96 | while (injector_server->HandleRequest() == 0) { 97 | } 98 | std::exit(1); 99 | } 100 | -------------------------------------------------------------------------------- /scuba/runtime_service/yaml_file_descriptor_factory.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include "scuba/runtime_service/yaml_file_descriptor_factory.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "argdata.hpp" 15 | #include "arpc++/arpc++.h" 16 | #include "flower/protocol/switchboard.ad.h" 17 | #include "yaml-cpp/exceptions.h" 18 | #include "yaml-cpp/mark.h" 19 | 20 | using arpc::ClientContext; 21 | using arpc::Status; 22 | using flower::protocol::switchboard::ConstrainRequest; 23 | using flower::protocol::switchboard::ConstrainResponse; 24 | using flower::protocol::switchboard::Right; 25 | using scuba::runtime_service::YAMLFileDescriptorFactory; 26 | 27 | const argdata_t* YAMLFileDescriptorFactory::GetNull(const YAML::Mark& mark) { 28 | return fallback_->GetNull(mark); 29 | } 30 | 31 | const argdata_t* YAMLFileDescriptorFactory::GetScalar(const YAML::Mark& mark, 32 | std::string_view tag, 33 | std::string_view value) { 34 | if (tag == "tag:nuxi.nl,2015:cloudabi/kubernetes/container_log") { 35 | return argdatas_.emplace_back(argdata_t::create_fd(container_log_->get())) 36 | .get(); 37 | } else if (tag == "tag:nuxi.nl,2015:cloudabi/kubernetes/mount") { 38 | auto lookup = mounts_->find(value); 39 | if (lookup == mounts_->end()) 40 | throw YAML::ParserException(mark, "Unknown volume mount"); 41 | return argdatas_.emplace_back(argdata_t::create_fd(lookup->second.get())) 42 | .get(); 43 | } else { 44 | return fallback_->GetScalar(mark, tag, value); 45 | } 46 | } 47 | 48 | const argdata_t* YAMLFileDescriptorFactory::GetSequence( 49 | const YAML::Mark& mark, std::string_view tag, 50 | std::vector elements) { 51 | return fallback_->GetSequence(mark, tag, std::move(elements)); 52 | } 53 | 54 | const argdata_t* YAMLFileDescriptorFactory::GetMap( 55 | const YAML::Mark& mark, std::string_view tag, 56 | std::vector keys, std::vector values) { 57 | if (tag == "tag:nuxi.nl,2015:cloudabi/kubernetes/server") { 58 | // Constraints to be placed on the switchboard connection that is 59 | // provided to the running process. 60 | ConstrainRequest request; 61 | request.add_rights(Right::SERVER_START); 62 | auto labels = request.mutable_in_labels(); 63 | (*labels)["server_kubernetes_namespace"] = pod_metadata_->namespace_(); 64 | (*labels)["server_kubernetes_pod_name"] = pod_metadata_->name(); 65 | (*labels)["server_kubernetes_pod_attempt"] = 66 | std::to_string(pod_metadata_->attempt()); 67 | (*labels)["server_kubernetes_container_name"] = container_metadata_->name(); 68 | (*labels)["server_kubernetes_container_attempt"] = 69 | std::to_string(container_metadata_->attempt()); 70 | for (size_t i = 0; i < keys.size(); ++i) { 71 | std::optional key = keys[i]->get_str(); 72 | std::optional value = values[i]->get_str(); 73 | if (!key || !value) 74 | throw YAML::ParserException( 75 | mark, "Switchboard label keys and values must be strings"); 76 | if (!labels->emplace(*key, *value).second) { 77 | std::ostringstream ss; 78 | ss << "Attempted to override predefined label \"" << *key << '"'; 79 | throw YAML::ParserException(mark, ss.str()); 80 | } 81 | } 82 | 83 | // Request a new switchboard connection. 84 | ClientContext context; 85 | ConstrainResponse response; 86 | if (Status status = 87 | switchboard_servers_->Constrain(&context, request, &response); 88 | !status.ok()) 89 | throw YAML::ParserException( 90 | mark, std::string("Failed to constrain switchboard channel: ") + 91 | status.error_message()); 92 | if (!response.switchboard()) 93 | throw YAML::ParserException( 94 | mark, "Switchboard did not return a file descriptor"); 95 | 96 | return argdatas_ 97 | .emplace_back(argdata_t::create_fd( 98 | fds_.emplace_back(response.switchboard())->get())) 99 | .get(); 100 | } else { 101 | return fallback_->GetMap(mark, tag, std::move(keys), std::move(values)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /scuba/image_service/image_service.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include "scuba/image_service/image_service.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "grpc++/grpc++.h" 16 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.grpc.pb.h" 17 | 18 | using grpc::ServerContext; 19 | using grpc::Status; 20 | using grpc::StatusCode; 21 | using runtime::Image; 22 | using runtime::ImageFsInfoRequest; 23 | using runtime::ImageFsInfoResponse; 24 | using runtime::ImageStatusRequest; 25 | using runtime::ImageStatusResponse; 26 | using runtime::ListImagesRequest; 27 | using runtime::ListImagesResponse; 28 | using runtime::PullImageRequest; 29 | using runtime::PullImageResponse; 30 | using runtime::RemoveImageRequest; 31 | using runtime::RemoveImageResponse; 32 | using scuba::image_service::ImageService; 33 | 34 | namespace { 35 | 36 | class DirDeleter { 37 | public: 38 | void operator()(DIR* directory) const { 39 | if (directory != nullptr) 40 | closedir(directory); 41 | } 42 | }; 43 | 44 | } // namespace 45 | 46 | Status ImageService::ListImages(ServerContext* context, 47 | const ListImagesRequest* request, 48 | ListImagesResponse* response) { 49 | std::unique_ptr directory( 50 | opendirat(image_directory_->get(), ".")); 51 | if (!directory) 52 | return {StatusCode::INTERNAL, std::strerror(errno)}; 53 | 54 | for (dirent* entry = readdir(directory.get()); entry != nullptr; 55 | entry = readdir(directory.get())) { 56 | // TODO(ed): Respect filter. 57 | if (IsLocalImageName_(entry->d_name)) { 58 | stat sb; 59 | if (fstatat(image_directory_->get(), entry->d_name, &sb, 60 | AT_SYMLINK_NOFOLLOW) == 0 && 61 | S_ISREG(sb.st_mode)) { 62 | Image* image = response->add_images(); 63 | image->set_id(entry->d_name); 64 | // TODO(ed): Set repo_tags. 65 | image->set_size(sb.st_size); 66 | } 67 | } else { 68 | // Filename doesn't match a supported image name pattern. It 69 | // could be a temporary file left by the image downloading 70 | // process. Remove it if it's too old, as it's likely stale. 71 | // TODO(ed): Implement. 72 | } 73 | } 74 | return Status::OK; 75 | } 76 | 77 | Status ImageService::ImageStatus(ServerContext* context, 78 | const ImageStatusRequest* request, 79 | ImageStatusResponse* response) { 80 | const std::string& image_name = request->image().image(); 81 | if (!IsLocalImageName_(image_name)) { 82 | // TODO(ed): Implement. 83 | return {StatusCode::UNIMPLEMENTED, "ImageStatus by URL not implemented"}; 84 | } 85 | 86 | stat sb; 87 | if (fstatat(image_directory_->get(), image_name.c_str(), &sb, 88 | AT_SYMLINK_NOFOLLOW) == 0 && 89 | S_ISREG(sb.st_mode)) { 90 | Image* image = response->mutable_image(); 91 | image->set_id(image_name); 92 | // TODO(ed): Set repo_tags. 93 | image->set_size(sb.st_size); 94 | } 95 | return Status::OK; 96 | } 97 | 98 | Status ImageService::PullImage(ServerContext* context, 99 | const PullImageRequest* request, 100 | PullImageResponse* response) { 101 | const std::string& image_name = request->image().image(); 102 | if (IsLocalImageName_(image_name)) { 103 | return {StatusCode::INVALID_ARGUMENT, 104 | "Images can only be pulled by URL, not by checksum. Try placing " 105 | "the image in the image directory manually."}; 106 | } 107 | return {StatusCode::UNIMPLEMENTED, "PullImage by URL not implemented"}; 108 | } 109 | 110 | Status ImageService::RemoveImage(ServerContext* context, 111 | const RemoveImageRequest* request, 112 | RemoveImageResponse* response) { 113 | const std::string& image_name = request->image().image(); 114 | if (!IsLocalImageName_(image_name)) { 115 | // TODO(ed): Look up canonical name. 116 | return {StatusCode::UNIMPLEMENTED, "RemoveImage by URL not implemented"}; 117 | } 118 | 119 | if (unlinkat(image_directory_->get(), image_name.c_str(), 0) != 0 && 120 | errno != ENOENT) 121 | return {StatusCode::INTERNAL, std::strerror(errno)}; 122 | return Status::OK; 123 | } 124 | 125 | Status ImageService::ImageFsInfo(ServerContext* context, 126 | const ImageFsInfoRequest* request, 127 | ImageFsInfoResponse* response) { 128 | return {StatusCode::UNIMPLEMENTED, "ImageFsInfo not implemented"}; 129 | } 130 | 131 | bool ImageService::IsLocalImageName_(std::string_view image_name) { 132 | return image_name.size() == 71 && image_name.substr(0, 7) == "sha256:" && 133 | std::all_of(image_name.begin() + 7, image_name.end(), [](char c) { 134 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); 135 | }); 136 | } 137 | -------------------------------------------------------------------------------- /scuba/runtime_service/runtime_service.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #ifndef SCUBA_RUNTIME_SERVICE_RUNTIME_SERVICE_H 6 | #define SCUBA_RUNTIME_SERVICE_RUNTIME_SERVICE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "arpc++/arpc++.h" 15 | #include "flower/protocol/switchboard.ad.h" 16 | #include "grpc++/grpc++.h" 17 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.grpc.pb.h" 18 | #include "scuba/runtime_service/pod_sandbox.h" 19 | 20 | namespace scuba { 21 | namespace runtime_service { 22 | 23 | class IPAddressAllocator; 24 | 25 | class RuntimeService final : public runtime::RuntimeService::Service { 26 | public: 27 | explicit RuntimeService( 28 | const arpc::FileDescriptor* root_directory, 29 | const arpc::FileDescriptor* image_directory, 30 | flower::protocol::switchboard::Switchboard::Stub* switchboard_servers, 31 | IPAddressAllocator* ip_address_allocator) 32 | : root_directory_(root_directory), 33 | image_directory_(image_directory), 34 | switchboard_servers_(switchboard_servers), 35 | ip_address_allocator_(ip_address_allocator) { 36 | } 37 | 38 | // Global state. 39 | grpc::Status Version(grpc::ServerContext* context, 40 | const runtime::VersionRequest* request, 41 | runtime::VersionResponse* response) override; 42 | grpc::Status Status(grpc::ServerContext* context, 43 | const runtime::StatusRequest* request, 44 | runtime::StatusResponse* response) override; 45 | 46 | // Pod management. 47 | grpc::Status RunPodSandbox(grpc::ServerContext* context, 48 | const runtime::RunPodSandboxRequest* request, 49 | runtime::RunPodSandboxResponse* response) override; 50 | grpc::Status StopPodSandbox( 51 | grpc::ServerContext* context, 52 | const runtime::StopPodSandboxRequest* request, 53 | runtime::StopPodSandboxResponse* response) override; 54 | grpc::Status RemovePodSandbox( 55 | grpc::ServerContext* context, 56 | const runtime::RemovePodSandboxRequest* request, 57 | runtime::RemovePodSandboxResponse* response) override; 58 | grpc::Status PodSandboxStatus( 59 | grpc::ServerContext* context, 60 | const runtime::PodSandboxStatusRequest* request, 61 | runtime::PodSandboxStatusResponse* response) override; 62 | grpc::Status ListPodSandbox( 63 | grpc::ServerContext* context, 64 | const runtime::ListPodSandboxRequest* request, 65 | runtime::ListPodSandboxResponse* response) override; 66 | 67 | // Container management. 68 | grpc::Status CreateContainer( 69 | grpc::ServerContext* context, 70 | const runtime::CreateContainerRequest* request, 71 | runtime::CreateContainerResponse* response) override; 72 | grpc::Status StartContainer( 73 | grpc::ServerContext* context, 74 | const runtime::StartContainerRequest* request, 75 | runtime::StartContainerResponse* response) override; 76 | grpc::Status StopContainer(grpc::ServerContext* context, 77 | const runtime::StopContainerRequest* request, 78 | runtime::StopContainerResponse* response) override; 79 | grpc::Status RemoveContainer( 80 | grpc::ServerContext* context, 81 | const runtime::RemoveContainerRequest* request, 82 | runtime::RemoveContainerResponse* response) override; 83 | grpc::Status ListContainers( 84 | grpc::ServerContext* context, 85 | const runtime::ListContainersRequest* request, 86 | runtime::ListContainersResponse* response) override; 87 | grpc::Status ContainerStatus( 88 | grpc::ServerContext* context, 89 | const runtime::ContainerStatusRequest* request, 90 | runtime::ContainerStatusResponse* response) override; 91 | 92 | // Misc. 93 | grpc::Status Attach(grpc::ServerContext* context, 94 | const runtime::AttachRequest* request, 95 | runtime::AttachResponse* response) override; 96 | grpc::Status PortForward(grpc::ServerContext* context, 97 | const runtime::PortForwardRequest* request, 98 | runtime::PortForwardResponse* response) override; 99 | grpc::Status UpdateRuntimeConfig( 100 | grpc::ServerContext* context, 101 | const runtime::UpdateRuntimeConfigRequest* request, 102 | runtime::UpdateRuntimeConfigResponse* response) override; 103 | 104 | private: 105 | const arpc::FileDescriptor* const root_directory_; 106 | const arpc::FileDescriptor* const image_directory_; 107 | flower::protocol::switchboard::Switchboard::Stub* const switchboard_servers_; 108 | IPAddressAllocator* const ip_address_allocator_; 109 | 110 | std::shared_mutex pod_sandboxes_lock_; 111 | std::map, std::less<>> 112 | pod_sandboxes_; 113 | 114 | RuntimeService(RuntimeService&) = delete; 115 | void operator=(RuntimeService) = delete; 116 | }; 117 | 118 | } // namespace runtime_service 119 | } // namespace scuba 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /scuba/runtime_service/pod_sandbox.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include "scuba/runtime_service/pod_sandbox.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "arpc++/arpc++.h" 23 | #include "flower/protocol/switchboard.ad.h" 24 | #include "google/protobuf/map.h" 25 | #include "scuba/runtime_service/ip_address_allocator.h" 26 | 27 | using arpc::FileDescriptor; 28 | using flower::protocol::switchboard::Switchboard; 29 | using google::protobuf::Map; 30 | using runtime::ContainerConfig; 31 | using runtime::ContainerState; 32 | using runtime::ContainerStatus; 33 | using runtime::PodSandboxConfig; 34 | using runtime::PodSandboxState; 35 | using runtime::PodSandboxStatus; 36 | using scuba::runtime_service::IPAddressLease; 37 | using scuba::runtime_service::PodSandbox; 38 | 39 | PodSandbox::PodSandbox(const PodSandboxConfig& config, 40 | IPAddressLease ip_address_lease) 41 | : metadata_(config.metadata()), 42 | log_directory_(config.log_directory()), 43 | creation_time_(std::chrono::system_clock::now()), 44 | labels_(config.labels()), 45 | annotations_(config.annotations()), 46 | ip_address_lease_(std::move(ip_address_lease)), 47 | state_(PodSandboxState::SANDBOX_READY) { 48 | } 49 | 50 | void PodSandbox::GetInfo(runtime::PodSandbox* info) { 51 | *info->mutable_metadata() = metadata_; 52 | info->set_created_at( 53 | std::chrono::nanoseconds(creation_time_.time_since_epoch()).count()); 54 | *info->mutable_labels() = labels_; 55 | *info->mutable_annotations() = annotations_; 56 | 57 | std::shared_lock lock(lock_); 58 | info->set_state(state_); 59 | } 60 | 61 | void PodSandbox::GetStatus(PodSandboxStatus* status) { 62 | *status->mutable_metadata() = metadata_; 63 | status->set_created_at(creation_time_.time_since_epoch().count()); 64 | status->mutable_network()->set_ip(ip_address_lease_.GetString()); 65 | *status->mutable_labels() = labels_; 66 | *status->mutable_annotations() = annotations_; 67 | 68 | std::shared_lock lock(lock_); 69 | status->set_state(state_); 70 | } 71 | 72 | void PodSandbox::Stop() { 73 | // Do a forced stop of all containers in the pod sandbox. Switch the 74 | // state to SANDBOX_NOTREADY. Otherwise, Kubernetes will not attempt 75 | // to destroy it. 76 | std::unique_lock lock(lock_); 77 | for (const auto& container : containers_) 78 | container.second->Stop(0); 79 | state_ = PodSandboxState::SANDBOX_NOTREADY; 80 | } 81 | 82 | bool PodSandbox::MatchesFilter(std::optional state, 83 | const Map& labels) { 84 | // Perform subset match on labels. We can't use std::includes() here, 85 | // as these maps use hashing. 86 | for (const auto& label : labels) { 87 | const auto& match = labels_.find(label.first); 88 | if (match == labels_.end() || label.second != match->second) 89 | return false; 90 | } 91 | if (!state) 92 | return true; 93 | std::shared_lock lock(lock_); 94 | return *state == state_; 95 | } 96 | 97 | void PodSandbox::CreateContainer(std::string_view container_id, 98 | const ContainerConfig& config) { 99 | std::unique_lock lock(lock_); 100 | if (state_ != PodSandboxState::SANDBOX_READY) 101 | throw std::logic_error(std::string(container_id) + 102 | " has already been terminated"); 103 | 104 | // Idempotence: only create the container if it doesn't exist yet. 105 | auto container = containers_.find(container_id); 106 | if (container == containers_.end()) 107 | containers_.insert( 108 | container, 109 | std::make_pair(container_id, std::make_unique(config))); 110 | } 111 | 112 | void PodSandbox::RemoveContainer(std::string_view container_id) { 113 | std::unique_lock lock(lock_); 114 | containers_.erase(containers_.find(container_id)); 115 | } 116 | 117 | void PodSandbox::StartContainer( 118 | std::string_view container_id, const FileDescriptor& root_directory, 119 | const FileDescriptor& image_directory, 120 | Switchboard::Stub* containers_switchboard_handle) { 121 | std::shared_lock lock(lock_); 122 | if (state_ != PodSandboxState::SANDBOX_READY) 123 | throw std::logic_error(std::string(container_id) + 124 | " has already been terminated"); 125 | 126 | auto container = containers_.find(container_id); 127 | if (container == containers_.end()) 128 | throw std::invalid_argument(std::string(container_id) + " does not exist"); 129 | 130 | // Turn provided log directory into a path relative to the root. 131 | const char* log_directory = log_directory_.c_str(); 132 | while (*log_directory == '/') 133 | ++log_directory; 134 | int fd = openat(root_directory.get(), log_directory, O_DIRECTORY | O_SEARCH); 135 | if (fd < 0) 136 | throw std::system_error(errno, std::system_category(), log_directory_); 137 | container->second->Start(metadata_, root_directory, image_directory, 138 | FileDescriptor(fd), containers_switchboard_handle); 139 | } 140 | 141 | bool PodSandbox::StopContainer(std::string_view container_id, 142 | std::int64_t timeout) { 143 | std::shared_lock lock(lock_); 144 | auto container = containers_.find(container_id); 145 | if (container == containers_.end()) 146 | return false; 147 | container->second->Stop(timeout); 148 | return true; 149 | } 150 | 151 | std::vector> 152 | PodSandbox::GetContainerInfo(std::string_view container_id, 153 | std::optional state, 154 | const Map& labels) { 155 | std::vector> infos; 156 | std::shared_lock lock(lock_); 157 | for (const auto& container : containers_) { 158 | // Apply filters. 159 | if (!container_id.empty() && container_id != container.first) 160 | continue; 161 | if (!container.second->MatchesFilter(state, labels)) 162 | continue; 163 | 164 | infos.emplace_back(container.first, runtime::Container{}); 165 | container.second->GetInfo(&infos.back().second); 166 | } 167 | return infos; 168 | } 169 | 170 | bool PodSandbox::GetContainerStatus(std::string_view container_id, 171 | ContainerStatus* status) { 172 | std::shared_lock lock(lock_); 173 | auto container = containers_.find(container_id); 174 | if (container == containers_.end()) 175 | return false; 176 | container->second->GetStatus(status); 177 | return true; 178 | } 179 | -------------------------------------------------------------------------------- /scuba/runtime_service/container.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include "scuba/runtime_service/container.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "argdata.hpp" 29 | #include "google/protobuf/map.h" 30 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.pb.h" 31 | #include "scuba/runtime_service/iso8601_timestamp.h" 32 | #include "scuba/runtime_service/pod_sandbox.h" 33 | #include "scuba/runtime_service/yaml_file_descriptor_factory.h" 34 | #include "scuba/util/fd_streambuf.h" 35 | #include "yaml2argdata/yaml_argdata_factory.h" 36 | #include "yaml2argdata/yaml_builder.h" 37 | #include "yaml2argdata/yaml_canonicalizing_factory.h" 38 | #include "yaml2argdata/yaml_error_factory.h" 39 | 40 | using arpc::FileDescriptor; 41 | using flower::protocol::switchboard::Switchboard; 42 | using google::protobuf::Map; 43 | using runtime::ContainerConfig; 44 | using runtime::ContainerState; 45 | using runtime::ContainerStatus; 46 | using runtime::PodSandboxMetadata; 47 | using scuba::runtime_service::Container; 48 | using scuba::util::fd_streambuf; 49 | using yaml2argdata::YAMLArgdataFactory; 50 | using yaml2argdata::YAMLBuilder; 51 | using yaml2argdata::YAMLCanonicalizingFactory; 52 | using yaml2argdata::YAMLErrorFactory; 53 | 54 | std::mutex Container::child_loop_lock_; 55 | uv_loop_t Container::child_loop_; 56 | 57 | Container::Container(const ContainerConfig& config) 58 | : metadata_(config.metadata()), 59 | image_(config.image()), 60 | creation_time_(std::chrono::system_clock::now()), 61 | labels_(config.labels()), 62 | annotations_(config.annotations()), 63 | mounts_(config.mounts()), 64 | log_path_(config.log_path()), 65 | argdata_(config.argdata()), 66 | container_state_(ContainerState::CONTAINER_CREATED) { 67 | // If this is the first container to be created, create an event loop 68 | // with which we can track termination of child processes. 69 | static std::once_flag child_loop_initialized; 70 | std::call_once(child_loop_initialized, []() { 71 | if (uv_loop_init(&child_loop_) != 0) 72 | std::terminate(); 73 | }); 74 | } 75 | 76 | Container::~Container() { 77 | std::unique_lock lock(child_loop_lock_); 78 | if (container_state_ != ContainerState::CONTAINER_CREATED) { 79 | // Child process spawned. Unregister process handle from the event loop. 80 | uv_close(reinterpret_cast(&child_process_), 81 | [](uv_handle_t* handle) { 82 | Container* container = 83 | reinterpret_cast(handle->data); 84 | container->container_state_ = ContainerState::CONTAINER_EXITED; 85 | }); 86 | uv_run(&child_loop_, UV_RUN_NOWAIT); 87 | assert(container_state_ == ContainerState::CONTAINER_EXITED && 88 | "Destroying container while the child process is running"); 89 | } 90 | } 91 | 92 | void Container::GetInfo(runtime::Container* info) { 93 | *info->mutable_metadata() = metadata_; 94 | *info->mutable_image() = image_; 95 | info->set_image_ref(image_.image()); 96 | *info->mutable_labels() = labels_; 97 | *info->mutable_annotations() = annotations_; 98 | 99 | std::unique_lock lock(child_loop_lock_); 100 | uv_run(&child_loop_, UV_RUN_NOWAIT); 101 | info->set_state(container_state_); 102 | info->set_created_at( 103 | std::chrono::nanoseconds(creation_time_.time_since_epoch()).count()); 104 | } 105 | 106 | void Container::GetStatus(ContainerStatus* status) { 107 | *status->mutable_metadata() = metadata_; 108 | *status->mutable_image() = image_; 109 | status->set_image_ref(image_.image()); 110 | *status->mutable_labels() = labels_; 111 | *status->mutable_annotations() = annotations_; 112 | *status->mutable_mounts() = mounts_; 113 | status->set_log_path(log_path_); 114 | // TODO(ed): reason, message. 115 | 116 | std::unique_lock lock(child_loop_lock_); 117 | uv_run(&child_loop_, UV_RUN_NOWAIT); 118 | status->set_state(container_state_); 119 | switch (container_state_) { 120 | case ContainerState::CONTAINER_EXITED: 121 | status->set_finished_at( 122 | std::chrono::nanoseconds(finish_time_.time_since_epoch()).count()); 123 | status->set_exit_code(exit_code_); 124 | [[fallthrough]]; 125 | case ContainerState::CONTAINER_RUNNING: 126 | status->set_started_at( 127 | std::chrono::nanoseconds(start_time_.time_since_epoch()).count()); 128 | [[fallthrough]]; 129 | case ContainerState::CONTAINER_CREATED: 130 | status->set_created_at( 131 | std::chrono::nanoseconds(creation_time_.time_since_epoch()).count()); 132 | break; 133 | default: 134 | assert(0 && "Container cannot be in an unknown state"); 135 | } 136 | } 137 | 138 | bool Container::MatchesFilter(std::optional state, 139 | const Map& labels) { 140 | // Perform subset match on labels. We can't use std::includes() here, 141 | // as these maps use hashing. 142 | for (const auto& label : labels) { 143 | const auto& match = labels_.find(label.first); 144 | if (match == labels_.end() || label.second != match->second) 145 | return false; 146 | } 147 | if (!state) 148 | return true; 149 | std::unique_lock lock(child_loop_lock_); 150 | uv_run(&child_loop_, UV_RUN_NOWAIT); 151 | return *state == container_state_; 152 | } 153 | 154 | void Container::Start(const PodSandboxMetadata& pod_metadata, 155 | const FileDescriptor& root_directory, 156 | const FileDescriptor& image_directory, 157 | const FileDescriptor& log_directory, 158 | Switchboard::Stub* containers_switchboard_handle) { 159 | // Idempotence: container may already have been started. 160 | std::unique_lock lock(child_loop_lock_); 161 | if (container_state_ != ContainerState::CONTAINER_CREATED) 162 | return; 163 | 164 | // Open the executable. 165 | // TODO(ed): This should validate the path. 166 | // TODO(ed): Compute executable checksum. 167 | int executable_fd = 168 | openat(image_directory.get(), image_.image().c_str(), O_EXEC); 169 | if (executable_fd < 0) 170 | throw std::system_error(errno, std::system_category(), image_.image()); 171 | FileDescriptor executable(executable_fd); 172 | 173 | std::unique_ptr container_log = 174 | OpenContainerLog_(log_directory); 175 | 176 | // Obtain file descriptors for every mount. 177 | std::map> mounts; 178 | for (const auto& mount : mounts_) { 179 | // TODO(ed): Pick proper O_ACCMODE. 180 | const char* host_path = mount.host_path().c_str(); 181 | while (*host_path == '/') 182 | ++host_path; 183 | int mount_fd = openat(root_directory.get(), host_path, O_SEARCH); 184 | if (mount_fd < 0) 185 | throw std::system_error(errno, std::system_category(), mount.host_path()); 186 | mounts.emplace(mount.container_path(), mount_fd); 187 | } 188 | 189 | // Convert Argdata in YAML form to serialized data. 190 | YAMLErrorFactory error_factory; 191 | YAMLFileDescriptorFactory file_descriptor_factory( 192 | &pod_metadata, &metadata_, container_log.get(), &mounts, 193 | containers_switchboard_handle, &error_factory); 194 | YAMLArgdataFactory argdata_factory(&file_descriptor_factory); 195 | YAMLCanonicalizingFactory canonicalizing_factory( 196 | &argdata_factory); 197 | YAMLBuilder builder(&canonicalizing_factory); 198 | std::istringstream argdata_stream(argdata_); 199 | const argdata_t* argdata = builder.Build(&argdata_stream); 200 | 201 | // Create a process handle through the event loop. 202 | child_process_.data = this; 203 | if (int error = program_spawn( 204 | &child_loop_, &child_process_, executable.get(), argdata, 205 | [](uv_process_t* process, int64_t exit_status, int term_signal) { 206 | Container* container = reinterpret_cast(process->data); 207 | container->container_state_ = ContainerState::CONTAINER_EXITED; 208 | container->finish_time_ = std::chrono::system_clock::now(); 209 | container->exit_code_ = 210 | term_signal == 0 ? exit_status : term_signal; 211 | }); 212 | error != 0) 213 | throw std::system_error(error, std::system_category(), 214 | "Failed to spawn process"); 215 | container_state_ = ContainerState::CONTAINER_RUNNING; 216 | start_time_ = std::chrono::system_clock::now(); 217 | } 218 | 219 | void Container::Stop(std::int64_t timeout) { 220 | std::unique_lock lock(child_loop_lock_); 221 | if (container_state_ != ContainerState::CONTAINER_CREATED) 222 | uv_process_kill(&child_process_, SIGKILL); 223 | } 224 | 225 | std::unique_ptr Container::OpenContainerLog_( 226 | const FileDescriptor& log_directory) { 227 | // Open logging output file. 228 | int logfile = openat(log_directory.get(), log_path_.c_str(), 229 | O_CREAT | O_WRONLY | O_TRUNC, 0666); 230 | if (logfile < 0) 231 | throw std::runtime_error(std::string("Failed to open logfile ") + 232 | std::string(log_path_)); 233 | auto logfd = std::make_unique(logfile); 234 | 235 | // Create pipe to which the container may write. 236 | int pipefds[2]; 237 | if (pipe(pipefds) != 0) 238 | throw std::system_error(errno, std::system_category(), 239 | "Failed to create pipe"); 240 | auto readfd = std::make_unique(pipefds[0]); 241 | auto writefd = std::make_unique(pipefds[1]); 242 | 243 | // Read messages from the pipe and write them into the log file in the 244 | // format that Kubernetes expects. 245 | std::thread([logfd{std::move(logfd)}, readfd{std::move(readfd)}]() mutable { 246 | // Startup message. 247 | fd_streambuf logstreambuf(std::move(logfd)); 248 | std::ostream logfile(&logstreambuf); 249 | logfile << ISO8601Timestamp() << " stderr --- Logging started" << std::endl; 250 | 251 | // Processing of logs written by the container. 252 | ssize_t input_length; 253 | bool line_start = true; 254 | for (;;) { 255 | char input_buffer[4096]; 256 | input_length = read(readfd->get(), input_buffer, sizeof(input_buffer)); 257 | if (input_length <= 0) 258 | break; 259 | 260 | std::optional now; 261 | for (char c : std::string_view(input_buffer, input_length)) { 262 | if (line_start) { 263 | if (!now) 264 | now = ISO8601Timestamp(); 265 | logfile << *now << " stdout "; 266 | line_start = false; 267 | } 268 | logfile << c; 269 | if (c == '\n') 270 | line_start = true; 271 | } 272 | logfile << std::flush; 273 | } 274 | if (!line_start) 275 | logfile << std::endl; 276 | 277 | // Termination message. 278 | logfile << ISO8601Timestamp() << " stderr --- Logging stopped: " 279 | << (input_length == 0 ? "Pipe closed by container" 280 | : std::strerror(errno)) 281 | << std::endl; 282 | }) 283 | .detach(); 284 | return writefd; 285 | } 286 | -------------------------------------------------------------------------------- /k8s.io/kubernetes/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /scuba/runtime_service/runtime_service.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nuxi, https://nuxi.nl/ 2 | // 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | 5 | #include "scuba/runtime_service/runtime_service.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "grpc++/grpc++.h" 16 | #include "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.grpc.pb.h" 17 | #include "scuba/runtime_service/naming_scheme.h" 18 | 19 | using grpc::ServerContext; 20 | using grpc::Status; 21 | using grpc::StatusCode; 22 | using runtime::AttachRequest; 23 | using runtime::AttachResponse; 24 | using runtime::ContainerConfig; 25 | using runtime::ContainerFilter; 26 | using runtime::ContainerState; 27 | using runtime::ContainerStatus; 28 | using runtime::ContainerStatusRequest; 29 | using runtime::ContainerStatusResponse; 30 | using runtime::CreateContainerRequest; 31 | using runtime::CreateContainerResponse; 32 | using runtime::ListContainersRequest; 33 | using runtime::ListContainersResponse; 34 | using runtime::ListPodSandboxRequest; 35 | using runtime::ListPodSandboxResponse; 36 | using runtime::PodSandboxConfig; 37 | using runtime::PodSandboxFilter; 38 | using runtime::PodSandboxState; 39 | using runtime::PodSandboxStatus; 40 | using runtime::PodSandboxStatusRequest; 41 | using runtime::PodSandboxStatusResponse; 42 | using runtime::PortForwardRequest; 43 | using runtime::PortForwardResponse; 44 | using runtime::RemoveContainerRequest; 45 | using runtime::RemoveContainerResponse; 46 | using runtime::RemovePodSandboxRequest; 47 | using runtime::RemovePodSandboxResponse; 48 | using runtime::RunPodSandboxRequest; 49 | using runtime::RunPodSandboxResponse; 50 | using runtime::RuntimeCondition; 51 | using runtime::RuntimeStatus; 52 | using runtime::StartContainerRequest; 53 | using runtime::StartContainerResponse; 54 | using runtime::StatusRequest; 55 | using runtime::StatusResponse; 56 | using runtime::StopContainerRequest; 57 | using runtime::StopContainerResponse; 58 | using runtime::StopPodSandboxRequest; 59 | using runtime::StopPodSandboxResponse; 60 | using runtime::UpdateRuntimeConfigRequest; 61 | using runtime::UpdateRuntimeConfigResponse; 62 | using runtime::VersionRequest; 63 | using runtime::VersionResponse; 64 | using scuba::runtime_service::RuntimeService; 65 | 66 | Status RuntimeService::Version(ServerContext* context, 67 | const VersionRequest* request, 68 | VersionResponse* response) { 69 | response->set_version("0.1.0"); 70 | response->set_runtime_name("scuba"); 71 | response->set_runtime_version("0.1"); 72 | response->set_runtime_api_version("v1alpha1"); 73 | return Status::OK; 74 | } 75 | 76 | Status RuntimeService::Status(ServerContext* context, 77 | const StatusRequest* request, 78 | StatusResponse* response) { 79 | // This environment is always runtime ready and network ready. 80 | RuntimeStatus* status = response->mutable_status(); 81 | RuntimeCondition* condition = status->add_conditions(); 82 | condition->set_type("RuntimeReady"); 83 | condition->set_status(true); 84 | 85 | condition = status->add_conditions(); 86 | condition->set_type("NetworkReady"); 87 | condition->set_status(true); 88 | return Status::OK; 89 | } 90 | 91 | Status RuntimeService::RunPodSandbox(ServerContext* context, 92 | const RunPodSandboxRequest* request, 93 | RunPodSandboxResponse* response) { 94 | const PodSandboxConfig& config = request->config(); 95 | std::string pod_sandbox_id = 96 | NamingScheme::CreatePodSandboxName(config.metadata()); 97 | 98 | // Idempotence: only create the pod sandbox if it doesn't exist yet. 99 | std::unique_lock lock(pod_sandboxes_lock_); 100 | auto pod_sandbox = pod_sandboxes_.find(pod_sandbox_id); 101 | if (pod_sandbox == pod_sandboxes_.end()) { 102 | IPAddressLease ip_address_lease; 103 | try { 104 | ip_address_lease = ip_address_allocator_->Allocate(); 105 | } catch (const std::exception& e) { 106 | return {StatusCode::INTERNAL, e.what()}; 107 | } 108 | pod_sandboxes_.insert( 109 | pod_sandbox, std::make_pair(pod_sandbox_id, 110 | std::make_unique( 111 | config, std::move(ip_address_lease)))); 112 | } 113 | 114 | response->set_pod_sandbox_id(pod_sandbox_id); 115 | return Status::OK; 116 | } 117 | 118 | Status RuntimeService::StopPodSandbox(ServerContext* context, 119 | const StopPodSandboxRequest* request, 120 | StopPodSandboxResponse* response) { 121 | std::shared_lock lock(pod_sandboxes_lock_); 122 | auto pod_sandbox = pod_sandboxes_.find(request->pod_sandbox_id()); 123 | if (pod_sandbox == pod_sandboxes_.end()) 124 | return {StatusCode::NOT_FOUND, "Pod sandbox does not exist"}; 125 | pod_sandbox->second->Stop(); 126 | return Status::OK; 127 | } 128 | 129 | Status RuntimeService::RemovePodSandbox(ServerContext* context, 130 | const RemovePodSandboxRequest* request, 131 | RemovePodSandboxResponse* response) { 132 | std::unique_lock lock(pod_sandboxes_lock_); 133 | pod_sandboxes_.erase(request->pod_sandbox_id()); 134 | return Status::OK; 135 | } 136 | 137 | Status RuntimeService::PodSandboxStatus(ServerContext* context, 138 | const PodSandboxStatusRequest* request, 139 | PodSandboxStatusResponse* response) { 140 | const std::string& pod_sandbox_id = request->pod_sandbox_id(); 141 | std::shared_lock lock(pod_sandboxes_lock_); 142 | auto pod_sandbox = pod_sandboxes_.find(pod_sandbox_id); 143 | if (pod_sandbox == pod_sandboxes_.end()) 144 | return {StatusCode::NOT_FOUND, "Pod sandbox does not exist"}; 145 | 146 | auto status = response->mutable_status(); 147 | pod_sandbox->second->GetStatus(status); 148 | status->set_id(pod_sandbox_id); 149 | return Status::OK; 150 | } 151 | 152 | Status RuntimeService::ListPodSandbox(ServerContext* context, 153 | const ListPodSandboxRequest* request, 154 | ListPodSandboxResponse* response) { 155 | const PodSandboxFilter& filter = request->filter(); 156 | const std::string& pod_sandbox_id = filter.id(); 157 | std::optional state; 158 | if (filter.has_state()) 159 | state = filter.state().state(); 160 | 161 | std::shared_lock lock(pod_sandboxes_lock_); 162 | for (const auto& pod_sandbox : pod_sandboxes_) { 163 | // Apply filters. 164 | if (!pod_sandbox_id.empty() && pod_sandbox_id != pod_sandbox.first) 165 | continue; 166 | if (!pod_sandbox.second->MatchesFilter(state, filter.label_selector())) 167 | continue; 168 | 169 | runtime::PodSandbox* info = response->add_items(); 170 | pod_sandbox.second->GetInfo(info); 171 | info->set_id(pod_sandbox.first); 172 | } 173 | return Status::OK; 174 | } 175 | 176 | Status RuntimeService::CreateContainer(ServerContext* context, 177 | const CreateContainerRequest* request, 178 | CreateContainerResponse* response) { 179 | std::shared_lock lock(pod_sandboxes_lock_); 180 | auto pod_sandbox = pod_sandboxes_.find(request->pod_sandbox_id()); 181 | if (pod_sandbox == pod_sandboxes_.end()) 182 | return {StatusCode::NOT_FOUND, "Pod sandbox does not exist"}; 183 | 184 | const ContainerConfig& config = request->config(); 185 | std::string container_id = 186 | NamingScheme::CreateContainerName(config.metadata()); 187 | pod_sandbox->second->CreateContainer(container_id, config); 188 | response->set_container_id(NamingScheme::ComposePodSandboxContainerName( 189 | pod_sandbox->first, container_id)); 190 | return Status::OK; 191 | } 192 | 193 | Status RuntimeService::StartContainer(ServerContext* context, 194 | const StartContainerRequest* request, 195 | StartContainerResponse* response) { 196 | auto ids = 197 | NamingScheme::DecomposePodSandboxContainerName(request->container_id()); 198 | std::shared_lock lock(pod_sandboxes_lock_); 199 | auto pod_sandbox = pod_sandboxes_.find(ids.first); 200 | if (pod_sandbox == pod_sandboxes_.end()) 201 | return {StatusCode::NOT_FOUND, "Pod sandbox does not exist"}; 202 | try { 203 | pod_sandbox->second->StartContainer( 204 | ids.second, *root_directory_, *image_directory_, switchboard_servers_); 205 | } catch (const std::invalid_argument& e) { 206 | return {StatusCode::INVALID_ARGUMENT, e.what()}; 207 | } catch (const std::exception& e) { 208 | return {StatusCode::INTERNAL, e.what()}; 209 | } 210 | return Status::OK; 211 | } 212 | 213 | Status RuntimeService::StopContainer(ServerContext* context, 214 | const StopContainerRequest* request, 215 | StopContainerResponse* response) { 216 | auto ids = 217 | NamingScheme::DecomposePodSandboxContainerName(request->container_id()); 218 | std::shared_lock lock(pod_sandboxes_lock_); 219 | auto pod_sandbox = pod_sandboxes_.find(ids.first); 220 | if (pod_sandbox == pod_sandboxes_.end()) 221 | return {StatusCode::NOT_FOUND, "Pod sandbox does not exist"}; 222 | if (!pod_sandbox->second->StopContainer(ids.second, request->timeout())) 223 | return {StatusCode::NOT_FOUND, "Container does not exist"}; 224 | return Status::OK; 225 | } 226 | 227 | Status RuntimeService::RemoveContainer(ServerContext* context, 228 | const RemoveContainerRequest* request, 229 | RemoveContainerResponse* response) { 230 | auto ids = 231 | NamingScheme::DecomposePodSandboxContainerName(request->container_id()); 232 | std::shared_lock lock(pod_sandboxes_lock_); 233 | auto pod_sandbox = pod_sandboxes_.find(ids.first); 234 | if (pod_sandbox != pod_sandboxes_.end()) 235 | pod_sandbox->second->RemoveContainer(ids.second); 236 | return Status::OK; 237 | } 238 | 239 | Status RuntimeService::ListContainers(ServerContext* context, 240 | const ListContainersRequest* request, 241 | ListContainersResponse* response) { 242 | const ContainerFilter& filter = request->filter(); 243 | auto ids = NamingScheme::DecomposePodSandboxContainerName(filter.id()); 244 | const std::string& pod_sandbox_id = filter.pod_sandbox_id(); 245 | std::optional state; 246 | if (filter.has_state()) 247 | state = filter.state().state(); 248 | 249 | std::shared_lock lock(pod_sandboxes_lock_); 250 | for (const auto& pod_sandbox : pod_sandboxes_) { 251 | // Apply filters. 252 | if (!pod_sandbox_id.empty() && pod_sandbox_id != pod_sandbox.first) 253 | continue; 254 | if (!ids.first.empty() && ids.first != pod_sandbox.first) 255 | continue; 256 | 257 | for (const auto& info_in : pod_sandbox.second->GetContainerInfo( 258 | ids.second, state, filter.label_selector())) { 259 | runtime::Container* info_out = response->add_containers(); 260 | *info_out = info_in.second; 261 | info_out->set_id(NamingScheme::ComposePodSandboxContainerName( 262 | pod_sandbox.first, info_in.first)); 263 | info_out->set_pod_sandbox_id(pod_sandbox.first); 264 | } 265 | } 266 | return Status::OK; 267 | } 268 | 269 | Status RuntimeService::ContainerStatus(ServerContext* context, 270 | const ContainerStatusRequest* request, 271 | ContainerStatusResponse* response) { 272 | const std::string& id = request->container_id(); 273 | auto ids = NamingScheme::DecomposePodSandboxContainerName(id); 274 | std::shared_lock lock(pod_sandboxes_lock_); 275 | auto pod_sandbox = pod_sandboxes_.find(ids.first); 276 | if (pod_sandbox == pod_sandboxes_.end()) 277 | return {StatusCode::NOT_FOUND, "Pod sandbox does not exist"}; 278 | auto status = response->mutable_status(); 279 | if (!pod_sandbox->second->GetContainerStatus(ids.second, status)) 280 | return {StatusCode::NOT_FOUND, "Container does not exist"}; 281 | status->set_id(id); 282 | return Status::OK; 283 | } 284 | 285 | Status RuntimeService::Attach(ServerContext* context, 286 | const AttachRequest* request, 287 | AttachResponse* response) { 288 | return {StatusCode::UNIMPLEMENTED, "Attach still needs to be implemented!"}; 289 | } 290 | 291 | Status RuntimeService::PortForward(ServerContext* context, 292 | const PortForwardRequest* request, 293 | PortForwardResponse* response) { 294 | return {StatusCode::UNIMPLEMENTED, 295 | "PortForward still needs to be implemented!"}; 296 | } 297 | 298 | Status RuntimeService::UpdateRuntimeConfig( 299 | ServerContext* context, const UpdateRuntimeConfigRequest* request, 300 | UpdateRuntimeConfigResponse* response) { 301 | if (!ip_address_allocator_->SetRange( 302 | request->runtime_config().network_config().pod_cidr())) 303 | return {StatusCode::INVALID_ARGUMENT, "Failed to parse IP range"}; 304 | return Status::OK; 305 | } 306 | -------------------------------------------------------------------------------- /k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime/api.proto: -------------------------------------------------------------------------------- 1 | // To regenerate api.pb.go run hack/update-generated-runtime.sh 2 | syntax = 'proto3'; 3 | 4 | package runtime; 5 | 6 | // Runtime service defines the public APIs for remote container runtimes 7 | service RuntimeService { 8 | // Version returns the runtime name, runtime version, and runtime API version. 9 | rpc Version(VersionRequest) returns (VersionResponse) {} 10 | 11 | // RunPodSandbox creates and starts a pod-level sandbox. Runtimes must ensure 12 | // the sandbox is in the ready state on success. 13 | rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {} 14 | // StopPodSandbox stops any running process that is part of the sandbox and 15 | // reclaims network resources (e.g., IP addresses) allocated to the sandbox. 16 | // If there are any running containers in the sandbox, they must be forcibly 17 | // terminated. 18 | // This call is idempotent, and must not return an error if all relevant 19 | // resources have already been reclaimed. kubelet will call StopPodSandbox 20 | // at least once before calling RemovePodSandbox. It will also attempt to 21 | // reclaim resources eagerly, as soon as a sandbox is not needed. Hence, 22 | // multiple StopPodSandbox calls are expected. 23 | rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {} 24 | // RemovePodSandbox removes the sandbox. If there are any running containers 25 | // in the sandbox, they must be forcibly terminated and removed. 26 | // This call is idempotent, and must not return an error if the sandbox has 27 | // already been removed. 28 | rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {} 29 | // PodSandboxStatus returns the status of the PodSandbox. If the PodSandbox is not 30 | // present, returns an error. 31 | rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {} 32 | // ListPodSandbox returns a list of PodSandboxes. 33 | rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {} 34 | 35 | // CreateContainer creates a new container in specified PodSandbox 36 | rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {} 37 | // StartContainer starts the container. 38 | rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {} 39 | // StopContainer stops a running container with a grace period (i.e., timeout). 40 | // This call is idempotent, and must not return an error if the container has 41 | // already been stopped. 42 | // TODO: what must the runtime do after the grace period is reached? 43 | rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {} 44 | // RemoveContainer removes the container. If the container is running, the 45 | // container must be forcibly removed. 46 | // This call is idempotent, and must not return an error if the container has 47 | // already been removed. 48 | rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {} 49 | // ListContainers lists all containers by filters. 50 | rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {} 51 | // ContainerStatus returns status of the container. If the container is not 52 | // present, returns an error. 53 | rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {} 54 | 55 | // ExecSync runs a command in a container synchronously. 56 | rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {} 57 | // Exec prepares a streaming endpoint to execute a command in the container. 58 | rpc Exec(ExecRequest) returns (ExecResponse) {} 59 | // Attach prepares a streaming endpoint to attach to a running container. 60 | rpc Attach(AttachRequest) returns (AttachResponse) {} 61 | // PortForward prepares a streaming endpoint to forward ports from a PodSandbox. 62 | rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {} 63 | 64 | // ContainerStats returns stats of the container. If the container does not 65 | // exist, the call returns an error. 66 | rpc ContainerStats(ContainerStatsRequest) returns (ContainerStatsResponse) {} 67 | // ListContainerStats returns stats of all running containers. 68 | rpc ListContainerStats(ListContainerStatsRequest) returns (ListContainerStatsResponse) {} 69 | 70 | // UpdateRuntimeConfig updates the runtime configuration based on the given request. 71 | rpc UpdateRuntimeConfig(UpdateRuntimeConfigRequest) returns (UpdateRuntimeConfigResponse) {} 72 | 73 | // Status returns the status of the runtime. 74 | rpc Status(StatusRequest) returns (StatusResponse) {} 75 | } 76 | 77 | // ImageService defines the public APIs for managing images. 78 | service ImageService { 79 | // ListImages lists existing images. 80 | rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {} 81 | // ImageStatus returns the status of the image. If the image is not 82 | // present, returns a response with ImageStatusResponse.Image set to 83 | // nil. 84 | rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {} 85 | // PullImage pulls an image with authentication config. 86 | rpc PullImage(PullImageRequest) returns (PullImageResponse) {} 87 | // RemoveImage removes the image. 88 | // This call is idempotent, and must not return an error if the image has 89 | // already been removed. 90 | rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {} 91 | // ImageFSInfo returns information of the filesystem that is used to store images. 92 | rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {} 93 | } 94 | 95 | message VersionRequest { 96 | // Version of the kubelet runtime API. 97 | string version = 1; 98 | } 99 | 100 | message VersionResponse { 101 | // Version of the kubelet runtime API. 102 | string version = 1; 103 | // Name of the container runtime. 104 | string runtime_name = 2; 105 | // Version of the container runtime. The string must be 106 | // semver-compatible. 107 | string runtime_version = 3; 108 | // API version of the container runtime. The string must be 109 | // semver-compatible. 110 | string runtime_api_version = 4; 111 | } 112 | 113 | // DNSConfig specifies the DNS servers and search domains of a sandbox. 114 | message DNSConfig { 115 | // List of DNS servers of the cluster. 116 | repeated string servers = 1; 117 | // List of DNS search domains of the cluster. 118 | repeated string searches = 2; 119 | // List of DNS options. See https://linux.die.net/man/5/resolv.conf 120 | // for all available options. 121 | repeated string options = 3; 122 | } 123 | 124 | enum Protocol { 125 | TCP = 0; 126 | UDP = 1; 127 | } 128 | 129 | // PortMapping specifies the port mapping configurations of a sandbox. 130 | message PortMapping { 131 | // Protocol of the port mapping. 132 | Protocol protocol = 1; 133 | // Port number within the container. Default: 0 (not specified). 134 | int32 container_port = 2; 135 | // Port number on the host. Default: 0 (not specified). 136 | int32 host_port = 3; 137 | // Host IP. 138 | string host_ip = 4; 139 | } 140 | 141 | // Mount specifies a host volume to mount into a container. 142 | message Mount { 143 | // Path of the mount within the container. 144 | string container_path = 1; 145 | // Path of the mount on the host. 146 | string host_path = 2; 147 | // If set, the mount is read-only. 148 | bool readonly = 3; 149 | // If set, the mount needs SELinux relabeling. 150 | bool selinux_relabel = 4; 151 | } 152 | 153 | // NamespaceOption provides options for Linux namespaces. 154 | message NamespaceOption { 155 | // If set, use the host's network namespace. 156 | bool host_network = 1; 157 | // If set, use the host's PID namespace. 158 | bool host_pid = 2; 159 | // If set, use the host's IPC namespace. 160 | bool host_ipc = 3; 161 | } 162 | 163 | // Int64Value is the wrapper of int64. 164 | message Int64Value { 165 | // The value. 166 | int64 value = 1; 167 | } 168 | 169 | // LinuxSandboxSecurityContext holds linux security configuration that will be 170 | // applied to a sandbox. Note that: 171 | // 1) It does not apply to containers in the pods. 172 | // 2) It may not be applicable to a PodSandbox which does not contain any running 173 | // process. 174 | message LinuxSandboxSecurityContext { 175 | // Configurations for the sandbox's namespaces. 176 | // This will be used only if the PodSandbox uses namespace for isolation. 177 | NamespaceOption namespace_options = 1; 178 | // Optional SELinux context to be applied. 179 | SELinuxOption selinux_options = 2; 180 | // UID to run sandbox processes as, when applicable. 181 | Int64Value run_as_user = 3; 182 | // If set, the root filesystem of the sandbox is read-only. 183 | bool readonly_rootfs = 4; 184 | // List of groups applied to the first process run in the sandbox, in 185 | // addition to the sandbox's primary GID. 186 | repeated int64 supplemental_groups = 5; 187 | // Indicates whether the sandbox will be asked to run a privileged 188 | // container. If a privileged container is to be executed within it, this 189 | // MUST be true. 190 | // This allows a sandbox to take additional security precautions if no 191 | // privileged containers are expected to be run. 192 | bool privileged = 6; 193 | // Seccomp profile for the sandbox, candidate values are: 194 | // * runtime/default: the default profile for the container runtime 195 | // * unconfined: unconfined profile, ie, no seccomp sandboxing 196 | // * localhost/: the profile installed on the node. 197 | // is the full path of the profile. 198 | string seccomp_profile_path = 7; 199 | } 200 | 201 | // LinuxPodSandboxConfig holds platform-specific configurations for Linux 202 | // host platforms and Linux-based containers. 203 | message LinuxPodSandboxConfig { 204 | // Parent cgroup of the PodSandbox. 205 | // The cgroupfs style syntax will be used, but the container runtime can 206 | // convert it to systemd semantics if needed. 207 | string cgroup_parent = 1; 208 | // LinuxSandboxSecurityContext holds sandbox security attributes. 209 | LinuxSandboxSecurityContext security_context = 2; 210 | // Sysctls holds linux sysctls config for the sandbox. 211 | map sysctls = 3; 212 | } 213 | 214 | // PodSandboxMetadata holds all necessary information for building the sandbox name. 215 | // The container runtime is encouraged to expose the metadata associated with the 216 | // PodSandbox in its user interface for better user experience. For example, 217 | // the runtime can construct a unique PodSandboxName based on the metadata. 218 | message PodSandboxMetadata { 219 | // Pod name of the sandbox. Same as the pod name in the PodSpec. 220 | string name = 1; 221 | // Pod UID of the sandbox. Same as the pod UID in the PodSpec. 222 | string uid = 2; 223 | // Pod namespace of the sandbox. Same as the pod namespace in the PodSpec. 224 | string namespace = 3; 225 | // Attempt number of creating the sandbox. Default: 0. 226 | uint32 attempt = 4; 227 | } 228 | 229 | // PodSandboxConfig holds all the required and optional fields for creating a 230 | // sandbox. 231 | message PodSandboxConfig { 232 | // Metadata of the sandbox. This information will uniquely identify the 233 | // sandbox, and the runtime should leverage this to ensure correct 234 | // operation. The runtime may also use this information to improve UX, such 235 | // as by constructing a readable name. 236 | PodSandboxMetadata metadata = 1; 237 | // Hostname of the sandbox. 238 | string hostname = 2; 239 | // Path to the directory on the host in which container log files are 240 | // stored. 241 | // By default the log of a container going into the LogDirectory will be 242 | // hooked up to STDOUT and STDERR. However, the LogDirectory may contain 243 | // binary log files with structured logging data from the individual 244 | // containers. For example, the files might be newline separated JSON 245 | // structured logs, systemd-journald journal files, gRPC trace files, etc. 246 | // E.g., 247 | // PodSandboxConfig.LogDirectory = `/var/log/pods//` 248 | // ContainerConfig.LogPath = `containerName_Instance#.log` 249 | // 250 | // WARNING: Log management and how kubelet should interface with the 251 | // container logs are under active discussion in 252 | // https://issues.k8s.io/24677. There *may* be future change of direction 253 | // for logging as the discussion carries on. 254 | string log_directory = 3; 255 | // DNS config for the sandbox. 256 | DNSConfig dns_config = 4; 257 | // Port mappings for the sandbox. 258 | repeated PortMapping port_mappings = 5; 259 | // Key-value pairs that may be used to scope and select individual resources. 260 | map labels = 6; 261 | // Unstructured key-value map that may be set by the kubelet to store and 262 | // retrieve arbitrary metadata. This will include any annotations set on a 263 | // pod through the Kubernetes API. 264 | // 265 | // Annotations MUST NOT be altered by the runtime; the annotations stored 266 | // here MUST be returned in the PodSandboxStatus associated with the pod 267 | // this PodSandboxConfig creates. 268 | // 269 | // In general, in order to preserve a well-defined interface between the 270 | // kubelet and the container runtime, annotations SHOULD NOT influence 271 | // runtime behaviour. 272 | // 273 | // Annotations can also be useful for runtime authors to experiment with 274 | // new features that are opaque to the Kubernetes APIs (both user-facing 275 | // and the CRI). Whenever possible, however, runtime authors SHOULD 276 | // consider proposing new typed fields for any new features instead. 277 | map annotations = 7; 278 | // Optional configurations specific to Linux hosts. 279 | LinuxPodSandboxConfig linux = 8; 280 | } 281 | 282 | message RunPodSandboxRequest { 283 | // Configuration for creating a PodSandbox. 284 | PodSandboxConfig config = 1; 285 | } 286 | 287 | message RunPodSandboxResponse { 288 | // ID of the PodSandbox to run. 289 | string pod_sandbox_id = 1; 290 | } 291 | 292 | message StopPodSandboxRequest { 293 | // ID of the PodSandbox to stop. 294 | string pod_sandbox_id = 1; 295 | } 296 | 297 | message StopPodSandboxResponse {} 298 | 299 | message RemovePodSandboxRequest { 300 | // ID of the PodSandbox to remove. 301 | string pod_sandbox_id = 1; 302 | } 303 | 304 | message RemovePodSandboxResponse {} 305 | 306 | message PodSandboxStatusRequest { 307 | // ID of the PodSandbox for which to retrieve status. 308 | string pod_sandbox_id = 1; 309 | } 310 | 311 | // PodSandboxNetworkStatus is the status of the network for a PodSandbox. 312 | message PodSandboxNetworkStatus { 313 | // IP address of the PodSandbox. 314 | string ip = 1; 315 | } 316 | 317 | // Namespace contains paths to the namespaces. 318 | message Namespace { 319 | // Namespace options for Linux namespaces. 320 | NamespaceOption options = 2; 321 | } 322 | 323 | // LinuxSandboxStatus contains status specific to Linux sandboxes. 324 | message LinuxPodSandboxStatus { 325 | // Paths to the sandbox's namespaces. 326 | Namespace namespaces = 1; 327 | } 328 | 329 | enum PodSandboxState { 330 | SANDBOX_READY = 0; 331 | SANDBOX_NOTREADY = 1; 332 | } 333 | 334 | // PodSandboxStatus contains the status of the PodSandbox. 335 | message PodSandboxStatus { 336 | // ID of the sandbox. 337 | string id = 1; 338 | // Metadata of the sandbox. 339 | PodSandboxMetadata metadata = 2; 340 | // State of the sandbox. 341 | PodSandboxState state = 3; 342 | // Creation timestamp of the sandbox in nanoseconds. Must be > 0. 343 | int64 created_at = 4; 344 | // Network contains network status if network is handled by the runtime. 345 | PodSandboxNetworkStatus network = 5; 346 | // Linux-specific status to a pod sandbox. 347 | LinuxPodSandboxStatus linux = 6; 348 | // Labels are key-value pairs that may be used to scope and select individual resources. 349 | map labels = 7; 350 | // Unstructured key-value map holding arbitrary metadata. 351 | // Annotations MUST NOT be altered by the runtime; the value of this field 352 | // MUST be identical to that of the corresponding PodSandboxConfig used to 353 | // instantiate the pod sandbox this status represents. 354 | map annotations = 8; 355 | } 356 | 357 | message PodSandboxStatusResponse { 358 | // Status of the PodSandbox. 359 | PodSandboxStatus status = 1; 360 | } 361 | 362 | // PodSandboxStateValue is the wrapper of PodSandboxState. 363 | message PodSandboxStateValue { 364 | // State of the sandbox. 365 | PodSandboxState state = 1; 366 | } 367 | 368 | // PodSandboxFilter is used to filter a list of PodSandboxes. 369 | // All those fields are combined with 'AND' 370 | message PodSandboxFilter { 371 | // ID of the sandbox. 372 | string id = 1; 373 | // State of the sandbox. 374 | PodSandboxStateValue state = 2; 375 | // LabelSelector to select matches. 376 | // Only api.MatchLabels is supported for now and the requirements 377 | // are ANDed. MatchExpressions is not supported yet. 378 | map label_selector = 3; 379 | } 380 | 381 | message ListPodSandboxRequest { 382 | // PodSandboxFilter to filter a list of PodSandboxes. 383 | PodSandboxFilter filter = 1; 384 | } 385 | 386 | 387 | // PodSandbox contains minimal information about a sandbox. 388 | message PodSandbox { 389 | // ID of the PodSandbox. 390 | string id = 1; 391 | // Metadata of the PodSandbox. 392 | PodSandboxMetadata metadata = 2; 393 | // State of the PodSandbox. 394 | PodSandboxState state = 3; 395 | // Creation timestamps of the PodSandbox in nanoseconds. Must be > 0. 396 | int64 created_at = 4; 397 | // Labels of the PodSandbox. 398 | map labels = 5; 399 | // Unstructured key-value map holding arbitrary metadata. 400 | // Annotations MUST NOT be altered by the runtime; the value of this field 401 | // MUST be identical to that of the corresponding PodSandboxConfig used to 402 | // instantiate this PodSandbox. 403 | map annotations = 6; 404 | } 405 | 406 | message ListPodSandboxResponse { 407 | // List of PodSandboxes. 408 | repeated PodSandbox items = 1; 409 | } 410 | 411 | // ImageSpec is an internal representation of an image. Currently, it wraps the 412 | // value of a Container's Image field (e.g. imageID or imageDigest), but in the 413 | // future it will include more detailed information about the different image types. 414 | message ImageSpec { 415 | string image = 1; 416 | } 417 | 418 | message KeyValue { 419 | string key = 1; 420 | string value = 2; 421 | } 422 | 423 | // LinuxContainerResources specifies Linux specific configuration for 424 | // resources. 425 | // TODO: Consider using Resources from opencontainers/runtime-spec/specs-go 426 | // directly. 427 | message LinuxContainerResources { 428 | // CPU CFS (Completely Fair Scheduler) period. Default: 0 (not specified). 429 | int64 cpu_period = 1; 430 | // CPU CFS (Completely Fair Scheduler) quota. Default: 0 (not specified). 431 | int64 cpu_quota = 2; 432 | // CPU shares (relative weight vs. other containers). Default: 0 (not specified). 433 | int64 cpu_shares = 3; 434 | // Memory limit in bytes. Default: 0 (not specified). 435 | int64 memory_limit_in_bytes = 4; 436 | // OOMScoreAdj adjusts the oom-killer score. Default: 0 (not specified). 437 | int64 oom_score_adj = 5; 438 | } 439 | 440 | // SELinuxOption are the labels to be applied to the container. 441 | message SELinuxOption { 442 | string user = 1; 443 | string role = 2; 444 | string type = 3; 445 | string level = 4; 446 | } 447 | 448 | // Capability contains the container capabilities to add or drop 449 | message Capability { 450 | // List of capabilities to add. 451 | repeated string add_capabilities = 1; 452 | // List of capabilities to drop. 453 | repeated string drop_capabilities = 2; 454 | } 455 | 456 | // LinuxContainerSecurityContext holds linux security configuration that will be applied to a container. 457 | message LinuxContainerSecurityContext { 458 | // Capabilities to add or drop. 459 | Capability capabilities = 1; 460 | // If set, run container in privileged mode. 461 | // Privileged mode is incompatible with the following options. If 462 | // privileged is set, the following features MAY have no effect: 463 | // 1. capabilities 464 | // 2. selinux_options 465 | // 4. seccomp 466 | // 5. apparmor 467 | // 468 | // Privileged mode implies the following specific options are applied: 469 | // 1. All capabilities are added. 470 | // 2. Sensitive paths, such as kernel module paths within sysfs, are not masked. 471 | // 3. Any sysfs and procfs mounts are mounted RW. 472 | // 4. Apparmor confinement is not applied. 473 | // 5. Seccomp restrictions are not applied. 474 | // 6. The device cgroup does not restrict access to any devices. 475 | // 7. All devices from the host's /dev are available within the container. 476 | // 8. SELinux restrictions are not applied (e.g. label=disabled). 477 | bool privileged = 2; 478 | // Configurations for the container's namespaces. 479 | // Only used if the container uses namespace for isolation. 480 | NamespaceOption namespace_options = 3; 481 | // SELinux context to be optionally applied. 482 | SELinuxOption selinux_options = 4; 483 | // UID to run the container process as. Only one of run_as_user and 484 | // run_as_username can be specified at a time. 485 | Int64Value run_as_user = 5; 486 | // User name to run the container process as. If specified, the user MUST 487 | // exist in the container image (i.e. in the /etc/passwd inside the image), 488 | // and be resolved there by the runtime; otherwise, the runtime MUST error. 489 | string run_as_username = 6; 490 | // If set, the root filesystem of the container is read-only. 491 | bool readonly_rootfs = 7; 492 | // List of groups applied to the first process run in the container, in 493 | // addition to the container's primary GID. 494 | repeated int64 supplemental_groups = 8; 495 | // AppArmor profile for the container, candidate values are: 496 | // * runtime/default: equivalent to not specifying a profile. 497 | // * localhost/: profile loaded on the node 498 | // (localhost) by name. The possible profile names are detailed at 499 | // http://wiki.apparmor.net/index.php/AppArmor_Core_Policy_Reference 500 | string apparmor_profile = 9; 501 | // Seccomp profile for the container, candidate values are: 502 | // * runtime/default: the default profile for the container runtime 503 | // * unconfined: unconfined profile, ie, no seccomp sandboxing 504 | // * localhost/: the profile installed on the node. 505 | // is the full path of the profile. 506 | string seccomp_profile_path = 10; 507 | // no_new_privs defines if the flag for no_new_privs should be set on the 508 | // container. 509 | bool no_new_privs = 11; 510 | } 511 | 512 | // LinuxContainerConfig contains platform-specific configuration for 513 | // Linux-based containers. 514 | message LinuxContainerConfig { 515 | // Resources specification for the container. 516 | LinuxContainerResources resources = 1; 517 | // LinuxContainerSecurityContext configuration for the container. 518 | LinuxContainerSecurityContext security_context = 2; 519 | } 520 | 521 | // ContainerMetadata holds all necessary information for building the container 522 | // name. The container runtime is encouraged to expose the metadata in its user 523 | // interface for better user experience. E.g., runtime can construct a unique 524 | // container name based on the metadata. Note that (name, attempt) is unique 525 | // within a sandbox for the entire lifetime of the sandbox. 526 | message ContainerMetadata { 527 | // Name of the container. Same as the container name in the PodSpec. 528 | string name = 1; 529 | // Attempt number of creating the container. Default: 0. 530 | uint32 attempt = 2; 531 | } 532 | 533 | // Device specifies a host device to mount into a container. 534 | message Device { 535 | // Path of the device within the container. 536 | string container_path = 1; 537 | // Path of the device on the host. 538 | string host_path = 2; 539 | // Cgroups permissions of the device, candidates are one or more of 540 | // * r - allows container to read from the specified device. 541 | // * w - allows container to write to the specified device. 542 | // * m - allows container to create device files that do not yet exist. 543 | string permissions = 3; 544 | } 545 | 546 | // ContainerConfig holds all the required and optional fields for creating a 547 | // container. 548 | message ContainerConfig { 549 | // Metadata of the container. This information will uniquely identify the 550 | // container, and the runtime should leverage this to ensure correct 551 | // operation. The runtime may also use this information to improve UX, such 552 | // as by constructing a readable name. 553 | ContainerMetadata metadata = 1 ; 554 | // Image to use. 555 | ImageSpec image = 2; 556 | // Command to execute (i.e., entrypoint for docker) 557 | repeated string command = 3; 558 | // Args for the Command (i.e., command for docker) 559 | repeated string args = 4; 560 | // Current working directory of the command. 561 | string working_dir = 5; 562 | // List of environment variable to set in the container. 563 | repeated KeyValue envs = 6; 564 | // Mounts for the container. 565 | repeated Mount mounts = 7; 566 | // Devices for the container. 567 | repeated Device devices = 8; 568 | // Key-value pairs that may be used to scope and select individual resources. 569 | // Label keys are of the form: 570 | // label-key ::= prefixed-name | name 571 | // prefixed-name ::= prefix '/' name 572 | // prefix ::= DNS_SUBDOMAIN 573 | // name ::= DNS_LABEL 574 | map labels = 9; 575 | // Unstructured key-value map that may be used by the kubelet to store and 576 | // retrieve arbitrary metadata. 577 | // 578 | // Annotations MUST NOT be altered by the runtime; the annotations stored 579 | // here MUST be returned in the ContainerStatus associated with the container 580 | // this ContainerConfig creates. 581 | // 582 | // In general, in order to preserve a well-defined interface between the 583 | // kubelet and the container runtime, annotations SHOULD NOT influence 584 | // runtime behaviour. 585 | map annotations = 10; 586 | // Path relative to PodSandboxConfig.LogDirectory for container to store 587 | // the log (STDOUT and STDERR) on the host. 588 | // E.g., 589 | // PodSandboxConfig.LogDirectory = `/var/log/pods//` 590 | // ContainerConfig.LogPath = `containerName_Instance#.log` 591 | // 592 | // WARNING: Log management and how kubelet should interface with the 593 | // container logs are under active discussion in 594 | // https://issues.k8s.io/24677. There *may* be future change of direction 595 | // for logging as the discussion carries on. 596 | string log_path = 11; 597 | 598 | // Variables for interactive containers, these have very specialized 599 | // use-cases (e.g. debugging). 600 | // TODO: Determine if we need to continue supporting these fields that are 601 | // part of Kubernetes's Container Spec. 602 | bool stdin = 12; 603 | bool stdin_once = 13; 604 | bool tty = 14; 605 | 606 | // Configuration specific to Linux containers. 607 | LinuxContainerConfig linux = 15; 608 | 609 | // CloudABI argument data that needs to be passed to the initial process. 610 | string argdata = 16; 611 | } 612 | 613 | message CreateContainerRequest { 614 | // ID of the PodSandbox in which the container should be created. 615 | string pod_sandbox_id = 1; 616 | // Config of the container. 617 | ContainerConfig config = 2; 618 | // Config of the PodSandbox. This is the same config that was passed 619 | // to RunPodSandboxRequest to create the PodSandbox. It is passed again 620 | // here just for easy reference. The PodSandboxConfig is immutable and 621 | // remains the same throughout the lifetime of the pod. 622 | PodSandboxConfig sandbox_config = 3; 623 | } 624 | 625 | message CreateContainerResponse { 626 | // ID of the created container. 627 | string container_id = 1; 628 | } 629 | 630 | message StartContainerRequest { 631 | // ID of the container to start. 632 | string container_id = 1; 633 | } 634 | 635 | message StartContainerResponse {} 636 | 637 | message StopContainerRequest { 638 | // ID of the container to stop. 639 | string container_id = 1; 640 | // Timeout in seconds to wait for the container to stop before forcibly 641 | // terminating it. Default: 0 (forcibly terminate the container immediately) 642 | int64 timeout = 2; 643 | } 644 | 645 | message StopContainerResponse {} 646 | 647 | message RemoveContainerRequest { 648 | // ID of the container to remove. 649 | string container_id = 1; 650 | } 651 | 652 | message RemoveContainerResponse {} 653 | 654 | enum ContainerState { 655 | CONTAINER_CREATED = 0; 656 | CONTAINER_RUNNING = 1; 657 | CONTAINER_EXITED = 2; 658 | CONTAINER_UNKNOWN = 3; 659 | } 660 | 661 | // ContainerStateValue is the wrapper of ContainerState. 662 | message ContainerStateValue { 663 | // State of the container. 664 | ContainerState state = 1; 665 | } 666 | 667 | // ContainerFilter is used to filter containers. 668 | // All those fields are combined with 'AND' 669 | message ContainerFilter { 670 | // ID of the container. 671 | string id = 1; 672 | // State of the container. 673 | ContainerStateValue state = 2; 674 | // ID of the PodSandbox. 675 | string pod_sandbox_id = 3; 676 | // LabelSelector to select matches. 677 | // Only api.MatchLabels is supported for now and the requirements 678 | // are ANDed. MatchExpressions is not supported yet. 679 | map label_selector = 4; 680 | } 681 | 682 | message ListContainersRequest { 683 | ContainerFilter filter = 1; 684 | } 685 | 686 | // Container provides the runtime information for a container, such as ID, hash, 687 | // state of the container. 688 | message Container { 689 | // ID of the container, used by the container runtime to identify 690 | // a container. 691 | string id = 1; 692 | // ID of the sandbox to which this container belongs. 693 | string pod_sandbox_id = 2; 694 | // Metadata of the container. 695 | ContainerMetadata metadata = 3; 696 | // Spec of the image. 697 | ImageSpec image = 4; 698 | // Reference to the image in use. For most runtimes, this should be an 699 | // image ID. 700 | string image_ref = 5; 701 | // State of the container. 702 | ContainerState state = 6; 703 | // Creation time of the container in nanoseconds. 704 | int64 created_at = 7; 705 | // Key-value pairs that may be used to scope and select individual resources. 706 | map labels = 8; 707 | // Unstructured key-value map holding arbitrary metadata. 708 | // Annotations MUST NOT be altered by the runtime; the value of this field 709 | // MUST be identical to that of the corresponding ContainerConfig used to 710 | // instantiate this Container. 711 | map annotations = 9; 712 | } 713 | 714 | message ListContainersResponse { 715 | // List of containers. 716 | repeated Container containers = 1; 717 | } 718 | 719 | message ContainerStatusRequest { 720 | // ID of the container for which to retrieve status. 721 | string container_id = 1; 722 | } 723 | 724 | // ContainerStatus represents the status of a container. 725 | message ContainerStatus { 726 | // ID of the container. 727 | string id = 1; 728 | // Metadata of the container. 729 | ContainerMetadata metadata = 2; 730 | // Status of the container. 731 | ContainerState state = 3; 732 | // Creation time of the container in nanoseconds. 733 | int64 created_at = 4; 734 | // Start time of the container in nanoseconds. Default: 0 (not specified). 735 | int64 started_at = 5; 736 | // Finish time of the container in nanoseconds. Default: 0 (not specified). 737 | int64 finished_at = 6; 738 | // Exit code of the container. Only required when finished_at != 0. Default: 0. 739 | int32 exit_code = 7; 740 | // Spec of the image. 741 | ImageSpec image = 8; 742 | // Reference to the image in use. For most runtimes, this should be an 743 | // image ID 744 | string image_ref = 9; 745 | // Brief CamelCase string explaining why container is in its current state. 746 | string reason = 10; 747 | // Human-readable message indicating details about why container is in its 748 | // current state. 749 | string message = 11; 750 | // Key-value pairs that may be used to scope and select individual resources. 751 | map labels = 12; 752 | // Unstructured key-value map holding arbitrary metadata. 753 | // Annotations MUST NOT be altered by the runtime; the value of this field 754 | // MUST be identical to that of the corresponding ContainerConfig used to 755 | // instantiate the Container this status represents. 756 | map annotations = 13; 757 | // Mounts for the container. 758 | repeated Mount mounts = 14; 759 | // Log path of container. 760 | string log_path = 15; 761 | } 762 | 763 | message ContainerStatusResponse { 764 | // Status of the container. 765 | ContainerStatus status = 1; 766 | } 767 | 768 | message ExecSyncRequest { 769 | // ID of the container. 770 | string container_id = 1; 771 | // Command to execute. 772 | repeated string cmd = 2; 773 | // Timeout in seconds to stop the command. Default: 0 (run forever). 774 | int64 timeout = 3; 775 | } 776 | 777 | message ExecSyncResponse { 778 | // Captured command stdout output. 779 | bytes stdout = 1; 780 | // Captured command stderr output. 781 | bytes stderr = 2; 782 | // Exit code the command finished with. Default: 0 (success). 783 | int32 exit_code = 3; 784 | } 785 | 786 | message ExecRequest { 787 | // ID of the container in which to execute the command. 788 | string container_id = 1; 789 | // Command to execute. 790 | repeated string cmd = 2; 791 | // Whether to exec the command in a TTY. 792 | bool tty = 3; 793 | // Whether to stream stdin. 794 | bool stdin = 4; 795 | } 796 | 797 | message ExecResponse { 798 | // Fully qualified URL of the exec streaming server. 799 | string url = 1; 800 | } 801 | 802 | message AttachRequest { 803 | // ID of the container to which to attach. 804 | string container_id = 1; 805 | // Whether to stream stdin. 806 | bool stdin = 2; 807 | // Whether the process being attached is running in a TTY. 808 | // This must match the TTY setting in the ContainerConfig. 809 | bool tty = 3; 810 | } 811 | 812 | message AttachResponse { 813 | // Fully qualified URL of the attach streaming server. 814 | string url = 1; 815 | } 816 | 817 | message PortForwardRequest { 818 | // ID of the container to which to forward the port. 819 | string pod_sandbox_id = 1; 820 | // Port to forward. 821 | repeated int32 port = 2; 822 | } 823 | 824 | message PortForwardResponse { 825 | // Fully qualified URL of the port-forward streaming server. 826 | string url = 1; 827 | } 828 | 829 | message ImageFilter { 830 | // Spec of the image. 831 | ImageSpec image = 1; 832 | } 833 | 834 | message ListImagesRequest { 835 | // Filter to list images. 836 | ImageFilter filter = 1; 837 | } 838 | 839 | // Basic information about a container image. 840 | message Image { 841 | // ID of the image. 842 | string id = 1; 843 | // Other names by which this image is known. 844 | repeated string repo_tags = 2; 845 | // Digests by which this image is known. 846 | repeated string repo_digests = 3; 847 | // Size of the image in bytes. Must be > 0. 848 | uint64 size = 4; 849 | // UID that will run the command(s). This is used as a default if no user is 850 | // specified when creating the container. UID and the following user name 851 | // are mutually exclusive. 852 | Int64Value uid = 5; 853 | // User name that will run the command(s). This is used if UID is not set 854 | // and no user is specified when creating container. 855 | string username = 6; 856 | } 857 | 858 | message ListImagesResponse { 859 | // List of images. 860 | repeated Image images = 1; 861 | } 862 | 863 | message ImageStatusRequest { 864 | // Spec of the image. 865 | ImageSpec image = 1; 866 | } 867 | 868 | message ImageStatusResponse { 869 | // Status of the image. 870 | Image image = 1; 871 | } 872 | 873 | // AuthConfig contains authorization information for connecting to a registry. 874 | message AuthConfig { 875 | string username = 1; 876 | string password = 2; 877 | string auth = 3; 878 | string server_address = 4; 879 | // IdentityToken is used to authenticate the user and get 880 | // an access token for the registry. 881 | string identity_token = 5; 882 | // RegistryToken is a bearer token to be sent to a registry 883 | string registry_token = 6; 884 | } 885 | 886 | message PullImageRequest { 887 | // Spec of the image. 888 | ImageSpec image = 1; 889 | // Authentication configuration for pulling the image. 890 | AuthConfig auth = 2; 891 | // Config of the PodSandbox, which is used to pull image in PodSandbox context. 892 | PodSandboxConfig sandbox_config = 3; 893 | } 894 | 895 | message PullImageResponse { 896 | // Reference to the image in use. For most runtimes, this should be an 897 | // image ID or digest. 898 | string image_ref = 1; 899 | } 900 | 901 | message RemoveImageRequest { 902 | // Spec of the image to remove. 903 | ImageSpec image = 1; 904 | } 905 | 906 | message RemoveImageResponse {} 907 | 908 | message NetworkConfig { 909 | // CIDR to use for pod IP addresses. 910 | string pod_cidr = 1; 911 | } 912 | 913 | message RuntimeConfig { 914 | NetworkConfig network_config = 1; 915 | } 916 | 917 | message UpdateRuntimeConfigRequest { 918 | RuntimeConfig runtime_config = 1; 919 | } 920 | 921 | message UpdateRuntimeConfigResponse {} 922 | 923 | // RuntimeCondition contains condition information for the runtime. 924 | // There are 2 kinds of runtime conditions: 925 | // 1. Required conditions: Conditions are required for kubelet to work 926 | // properly. If any required condition is unmet, the node will be not ready. 927 | // The required conditions include: 928 | // * RuntimeReady: RuntimeReady means the runtime is up and ready to accept 929 | // basic containers e.g. container only needs host network. 930 | // * NetworkReady: NetworkReady means the runtime network is up and ready to 931 | // accept containers which require container network. 932 | // 2. Optional conditions: Conditions are informative to the user, but kubelet 933 | // will not rely on. Since condition type is an arbitrary string, all conditions 934 | // not required are optional. These conditions will be exposed to users to help 935 | // them understand the status of the system. 936 | message RuntimeCondition { 937 | // Type of runtime condition. 938 | string type = 1; 939 | // Status of the condition, one of true/false. Default: false. 940 | bool status = 2; 941 | // Brief CamelCase string containing reason for the condition's last transition. 942 | string reason = 3; 943 | // Human-readable message indicating details about last transition. 944 | string message = 4; 945 | } 946 | 947 | // RuntimeStatus is information about the current status of the runtime. 948 | message RuntimeStatus { 949 | // List of current observed runtime conditions. 950 | repeated RuntimeCondition conditions = 1; 951 | } 952 | 953 | message StatusRequest {} 954 | 955 | message StatusResponse { 956 | // Status of the Runtime. 957 | RuntimeStatus status = 1; 958 | } 959 | 960 | message ImageFsInfoRequest {} 961 | 962 | // UInt64Value is the wrapper of uint64. 963 | message UInt64Value { 964 | // The value. 965 | uint64 value = 1; 966 | } 967 | 968 | // StorageIdentifier uniquely identify the storage.. 969 | message StorageIdentifier{ 970 | // UUID of the device. 971 | string uuid = 1; 972 | } 973 | 974 | // FilesystemUsage provides the filesystem usage information. 975 | message FilesystemUsage { 976 | // Timestamp in nanoseconds at which the information were collected. Must be > 0. 977 | int64 timestamp = 1; 978 | // The underlying storage of the filesystem. 979 | StorageIdentifier storage_id = 2; 980 | // UsedBytes represents the bytes used for images on the filesystem. 981 | // This may differ from the total bytes used on the filesystem and may not 982 | // equal CapacityBytes - AvailableBytes. 983 | UInt64Value used_bytes = 3; 984 | // InodesUsed represents the inodes used by the images. 985 | // This may not equal InodesCapacity - InodesAvailable because the underlying 986 | // filesystem may also be used for purposes other than storing images. 987 | UInt64Value inodes_used = 4; 988 | } 989 | 990 | message ImageFsInfoResponse { 991 | // Information of image filesystem(s). 992 | repeated FilesystemUsage image_filesystems = 1; 993 | } 994 | 995 | message ContainerStatsRequest{ 996 | // ID of the container for which to retrieve stats. 997 | string container_id = 1; 998 | } 999 | 1000 | message ContainerStatsResponse { 1001 | // Stats of the container. 1002 | ContainerStats stats = 1; 1003 | } 1004 | 1005 | message ListContainerStatsRequest{ 1006 | // Filter for the list request. 1007 | ContainerStatsFilter filter = 1; 1008 | } 1009 | 1010 | // ContainerStatsFilter is used to filter containers. 1011 | // All those fields are combined with 'AND' 1012 | message ContainerStatsFilter { 1013 | // ID of the container. 1014 | string id = 1; 1015 | // ID of the PodSandbox. 1016 | string pod_sandbox_id = 2; 1017 | // LabelSelector to select matches. 1018 | // Only api.MatchLabels is supported for now and the requirements 1019 | // are ANDed. MatchExpressions is not supported yet. 1020 | map label_selector = 3; 1021 | } 1022 | 1023 | message ListContainerStatsResponse { 1024 | // Stats of the container. 1025 | repeated ContainerStats stats = 1; 1026 | } 1027 | 1028 | // ContainerAttributes provides basic information of the container. 1029 | message ContainerAttributes { 1030 | // ID of the container. 1031 | string id = 1; 1032 | // Metadata of the container. 1033 | ContainerMetadata metadata = 2; 1034 | // Key-value pairs that may be used to scope and select individual resources. 1035 | map labels = 3; 1036 | // Unstructured key-value map holding arbitrary metadata. 1037 | // Annotations MUST NOT be altered by the runtime; the value of this field 1038 | // MUST be identical to that of the corresponding ContainerConfig used to 1039 | // instantiate the Container this status represents. 1040 | map annotations = 4; 1041 | } 1042 | 1043 | // ContainerStats provides the resource usage statistics for a container. 1044 | message ContainerStats { 1045 | // Information of the container. 1046 | ContainerAttributes attributes = 1; 1047 | // CPU usage gathered from the container. 1048 | CpuUsage cpu = 2; 1049 | // Memory usage gathered from the container. 1050 | MemoryUsage memory = 3; 1051 | // Usage of the writeable layer. 1052 | FilesystemUsage writable_layer = 4; 1053 | } 1054 | 1055 | // CpuUsage provides the CPU usage information. 1056 | message CpuUsage { 1057 | // Timestamp in nanoseconds at which the information were collected. Must be > 0. 1058 | int64 timestamp = 1; 1059 | // Cumulative CPU usage (sum across all cores) since object creation. 1060 | UInt64Value usage_core_nano_seconds = 2; 1061 | } 1062 | 1063 | // MemoryUsage provides the memory usage information. 1064 | message MemoryUsage { 1065 | // Timestamp in nanoseconds at which the information were collected. Must be > 0. 1066 | int64 timestamp = 1; 1067 | // The amount of working set memory in bytes. 1068 | UInt64Value working_set_bytes = 2; 1069 | } 1070 | --------------------------------------------------------------------------------