diff --git a/.ko.yaml b/.ko.yaml index 774665cec..e95fcd58e 100644 --- a/.ko.yaml +++ b/.ko.yaml @@ -21,3 +21,8 @@ defaultPlatforms: baseImageOverrides: github.com/agent-substrate/substrate/demos/sandbox: alpine github.com/agent-substrate/substrate/demos/agent-secret: alpine + # ateom-cloud-hypervisor shells out to nsenter + tar (to capture the kata + # shim's virtio-fs shared dir) and execs the fetched cloud-hypervisor/virtiofsd + # (Rust, glibc-linked) binaries. debian-slim provides util-linux (nsenter), + # tar, and glibc — NOT distroless/static. Kata/CH assets are fetched at runtime. + github.com/agent-substrate/substrate/cmd/ateom-cloud-hypervisor: debian:stable-slim diff --git a/LICENSES/github.com/containerd/containerd/api/LICENSE b/LICENSES/github.com/containerd/containerd/api/LICENSE new file mode 100644 index 000000000..584149b6e --- /dev/null +++ b/LICENSES/github.com/containerd/containerd/api/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright The containerd Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSES/github.com/containerd/log/LICENSE b/LICENSES/github.com/containerd/log/LICENSE new file mode 100644 index 000000000..584149b6e --- /dev/null +++ b/LICENSES/github.com/containerd/log/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright The containerd Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSES/github.com/containerd/ttrpc/LICENSE b/LICENSES/github.com/containerd/ttrpc/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSES/github.com/containerd/ttrpc/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/cmd/ateapi/internal/controlapi/workflow_resume.go b/cmd/ateapi/internal/controlapi/workflow_resume.go index 9f06eec0d..42298e472 100644 --- a/cmd/ateapi/internal/controlapi/workflow_resume.go +++ b/cmd/ateapi/internal/controlapi/workflow_resume.go @@ -33,6 +33,26 @@ import ( "k8s.io/client-go/kubernetes" ) +// buildRuntimeAssetsConfig maps an ActorTemplate RuntimeConfig to the ateletpb +// RuntimeAssetsConfig atelet uses to fetch a non-gVisor runtime's assets. +// Returns nil for gVisor (empty type / no assets), so the runsc path is used. +func buildRuntimeAssetsConfig(rt atev1alpha1.RuntimeConfig) *ateletpb.RuntimeAssetsConfig { + if rt.Type == "" || rt.Type == string(atev1alpha1.RuntimeGvisor) || len(rt.Assets) == 0 { + return nil + } + cfg := &ateletpb.RuntimeAssetsConfig{ + Runtime: rt.Type, + Assets: make(map[string]*ateletpb.RuntimeAsset, len(rt.Assets)), + } + for name, a := range rt.Assets { + cfg.Assets[name] = &ateletpb.RuntimeAsset{Sha256Hash: a.SHA256Hash, Url: a.URL} + } + if rt.Authentication.GCP != nil { + cfg.Authentication = &ateletpb.AuthenticationConfig{Gcp: &ateletpb.GCPAuthenticationConfig{Use: true}} + } + return cfg +} + // ResumeInput holds the immutable parameters requested by the client. type ResumeInput struct { ActorID string @@ -199,6 +219,8 @@ func (s *CallAteletRestoreStep) Execute(ctx context.Context, input *ResumeInput, runscCfg.Authentication = authnCfg } + runtimeCfg := buildRuntimeAssetsConfig(state.ActorTemplate.Spec.Runtime) + if state.Actor.LastSnapshot != "" { slog.InfoContext(ctx, "Actor has snapshot; Restoring from snapshot") @@ -208,6 +230,7 @@ func (s *CallAteletRestoreStep) Execute(ctx context.Context, input *ResumeInput, ActorTemplateName: state.Actor.GetActorTemplateName(), ActorId: state.Actor.GetActorId(), Runsc: runscCfg, + RuntimeAssets: runtimeCfg, Spec: workloadSpec, SnapshotUriPrefix: state.Actor.GetLastSnapshot(), } @@ -227,6 +250,7 @@ func (s *CallAteletRestoreStep) Execute(ctx context.Context, input *ResumeInput, ActorTemplateName: state.Actor.GetActorTemplateName(), ActorId: state.Actor.GetActorId(), Runsc: runscCfg, + RuntimeAssets: runtimeCfg, Spec: workloadSpec, SnapshotUriPrefix: snapshot, } @@ -243,6 +267,7 @@ func (s *CallAteletRestoreStep) Execute(ctx context.Context, input *ResumeInput, ActorTemplateName: state.Actor.GetActorTemplateName(), ActorId: state.Actor.GetActorId(), Runsc: runscCfg, + RuntimeAssets: runtimeCfg, Spec: workloadSpec, } _, err = client.Run(ctx, req) diff --git a/cmd/ateapi/internal/controlapi/workflow_suspend.go b/cmd/ateapi/internal/controlapi/workflow_suspend.go index d8210574b..ee7ac614f 100644 --- a/cmd/ateapi/internal/controlapi/workflow_suspend.go +++ b/cmd/ateapi/internal/controlapi/workflow_suspend.go @@ -141,6 +141,7 @@ func (s *CallAteletSuspendStep) Execute(ctx context.Context, input *SuspendInput ActorTemplateName: state.Actor.GetActorTemplateName(), ActorId: state.Actor.GetActorId(), Runsc: runscCfg, + RuntimeAssets: buildRuntimeAssetsConfig(state.ActorTemplate.Spec.Runtime), Spec: &ateletpb.WorkloadSpec{ PauseImage: state.ActorTemplate.Spec.PauseImage, }, diff --git a/cmd/atelet/main.go b/cmd/atelet/main.go index c4425bade..cf7b3aaa9 100644 --- a/cmd/atelet/main.go +++ b/cmd/atelet/main.go @@ -19,6 +19,7 @@ import ( "context" "crypto/sha256" "encoding/hex" + "encoding/json" "errors" "fmt" "log/slog" @@ -283,6 +284,81 @@ func (s *AteomHerder) fetchRunsc(ctx context.Context, cfg *ateletpb.RunscConfig) return localPath, nil } +// fetchRuntimeForRequest resolves the runtime assets for a request. For a +// non-gVisor runtime (RuntimeAssets set) it fetches the asset set and returns +// (name->localPath) with an empty runscPath; otherwise it fetches runsc and +// returns its path. Exactly one of the two is populated. +func (s *AteomHerder) fetchRuntimeForRequest(ctx context.Context, runsc *ateletpb.RunscConfig, rt *ateletpb.RuntimeAssetsConfig) (runscPath string, assetPaths map[string]string, err error) { + if rt != nil && rt.GetRuntime() != "" && rt.GetRuntime() != "gvisor" { + paths, err := s.fetchRuntimeAssets(ctx, rt) + return "", paths, err + } + p, err := s.fetchRunscAndPrep(ctx, runsc) + return p, nil, err +} + +// fetchRuntimeAssets fetches every asset in cfg content-addressed (like runsc), +// caching by sha256 under the static-files dir, and returns name->local path for +// atelet to pass to ateom. +func (s *AteomHerder) fetchRuntimeAssets(ctx context.Context, cfg *ateletpb.RuntimeAssetsConfig) (map[string]string, error) { + if err := os.MkdirAll(ateompath.StaticFilesDir, 0o700); err != nil { + return nil, fmt.Errorf("while creating static files dir: %w", err) + } + client := s.anonGCSClient + if cfg.GetAuthentication().GetGcp().GetUse() { + client = s.gcsClient + } + paths := make(map[string]string, len(cfg.GetAssets())) + for name, asset := range cfg.GetAssets() { + localPath := ateompath.RuntimeAssetPath(name, asset.GetSha256Hash()) + if _, err := os.Stat(localPath); err == nil { + paths[name] = localPath + continue + } else if !errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("while stat-ing %q: %w", localPath, err) + } + content, err := ategcs.FetchFromGCS(ctx, client, asset.GetUrl()) + if err != nil { + return nil, fmt.Errorf("while fetching asset %q from %v: %w", name, asset.GetUrl(), err) + } + sum := sha256.Sum256(content) + wantSum, err := hex.DecodeString(asset.GetSha256Hash()) + if err != nil { + return nil, fmt.Errorf("while parsing sha256 for %q: %w", name, err) + } + if !bytes.Equal(sum[:], wantSum) { + return nil, fmt.Errorf("sha256 mismatch for asset %q; got=%s want=%s", name, hex.EncodeToString(sum[:]), asset.GetSha256Hash()) + } + if err := atomicWriteFile(localPath, content, 0o755); err != nil { + return nil, fmt.Errorf("while writing asset %q: %w", name, err) + } + paths[name] = localPath + } + return paths, nil +} + +// atomicWriteFile writes content to a temp file in the same dir then renames it +// into place with the given mode. +func atomicWriteFile(path string, content []byte, mode os.FileMode) error { + tmp, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)+"-download-") + if err != nil { + return err + } + defer os.Remove(tmp.Name()) + if _, err := tmp.Write(content); err != nil { + tmp.Close() + return err + } + if err := tmp.Chmod(mode); err != nil { + tmp.Close() + return err + } + if err := tmp.Close(); err != nil { + return err + } + return os.Rename(tmp.Name(), path) +} + func (s *AteomHerder) Run(ctx context.Context, req *ateletpb.RunRequest) (*ateletpb.RunResponse, error) { if err := validateRunRequest(req); err != nil { // status.Error so the interceptor surfaces InvalidArgument and the @@ -290,7 +366,7 @@ func (s *AteomHerder) Run(ctx context.Context, req *ateletpb.RunRequest) (*atele return nil, status.Error(codes.InvalidArgument, err.Error()) } - runscPath, err := s.fetchRunscAndPrep(ctx, req.GetRunsc()) + runscPath, runtimeAssetPaths, err := s.fetchRuntimeForRequest(ctx, req.GetRunsc(), req.GetRuntimeAssets()) if err != nil { return nil, err } @@ -318,6 +394,7 @@ func (s *AteomHerder) Run(ctx context.Context, req *ateletpb.RunRequest) (*atele ActorTemplateName: req.GetActorTemplateName(), ActorId: req.GetActorId(), RunscPath: runscPath, + RuntimeAssetPaths: runtimeAssetPaths, Spec: buildAteomWorkloadSpec(req.GetSpec()), }); err != nil { return nil, fmt.Errorf("while calling ateom.RunWorkload: %w", err) @@ -367,7 +444,7 @@ func (s *AteomHerder) Checkpoint(ctx context.Context, req *ateletpb.CheckpointRe return nil, status.Error(codes.InvalidArgument, err.Error()) } - runscPath, err := s.fetchRunscAndPrep(ctx, req.GetRunsc()) + runscPath, runtimeAssetPaths, err := s.fetchRuntimeForRequest(ctx, req.GetRunsc(), req.GetRuntimeAssets()) if err != nil { return nil, err } @@ -380,46 +457,55 @@ func (s *AteomHerder) Checkpoint(ctx context.Context, req *ateletpb.CheckpointRe } // Tell ateom to take checkpoint and delete containers. - if _, err := client.CheckpointWorkload(ctx, &ateompb.CheckpointWorkloadRequest{ + resp, err := client.CheckpointWorkload(ctx, &ateompb.CheckpointWorkloadRequest{ ActorTemplateNamespace: req.GetActorTemplateNamespace(), ActorTemplateName: req.GetActorTemplateName(), ActorId: req.GetActorId(), RunscPath: runscPath, + RuntimeAssetPaths: runtimeAssetPaths, Spec: buildAteomWorkloadSpec(req.GetSpec()), - }); err != nil { + }) + if err != nil { return nil, fmt.Errorf("while calling ateom.CheckpointWorkload: %w", err) } prefix := strings.TrimSuffix(req.GetSnapshotUriPrefix(), "/") ns, tmpl, actorID := req.GetActorTemplateNamespace(), req.GetActorTemplateName(), req.GetActorId() - checkpointImgPath := filepath.Join(checkpointDir, "checkpoint.img") - pagesImgPath := filepath.Join(checkpointDir, "pages.img") - pagesMetaImgPath := filepath.Join(checkpointDir, "pages_meta.img") - - recordSnapshotSize(ctx, "checkpoint", checkpointImgPath, ns, tmpl) - - // Upload checkpoint from local dir. - if err := ategcs.SendLocalFileToGCSWithZstd(ctx, s.gcsClient, - prefix+"/checkpoint.img.zstd", - checkpointImgPath, - ); err != nil { - return nil, fmt.Errorf("while uploading checkpoint.img to GCS: %w", err) - } - - recordSnapshotSize(ctx, "pages", pagesImgPath, ns, tmpl) - if err := uploadIfExists(ctx, s.gcsClient, - prefix+"/pages.img.zstd", - pagesImgPath, - ); err != nil { - return nil, err - } - recordSnapshotSize(ctx, "pages_meta", pagesMetaImgPath, ns, tmpl) - if err := uploadIfExists(ctx, s.gcsClient, - prefix+"/pages_meta.img.zstd", - pagesMetaImgPath, - ); err != nil { - return nil, err + if files := resp.GetSnapshotFiles(); len(files) > 0 { + // Runtime reported its own snapshot file set (e.g. cloud-hypervisor): + // ship exactly those files + a manifest. + recordSnapshotSize(ctx, "memory-ranges", filepath.Join(checkpointDir, "memory-ranges"), ns, tmpl) + if err := uploadSnapshotFiles(ctx, s.gcsClient, prefix, checkpointDir, files); err != nil { + return nil, err + } + } else { + // Legacy fixed file set (gVisor). + checkpointImgPath := filepath.Join(checkpointDir, "checkpoint.img") + pagesImgPath := filepath.Join(checkpointDir, "pages.img") + pagesMetaImgPath := filepath.Join(checkpointDir, "pages_meta.img") + + recordSnapshotSize(ctx, "checkpoint", checkpointImgPath, ns, tmpl) + if err := ategcs.SendLocalFileToGCSWithZstd(ctx, s.gcsClient, + prefix+"/checkpoint.img.zstd", + checkpointImgPath, + ); err != nil { + return nil, fmt.Errorf("while uploading checkpoint.img to GCS: %w", err) + } + recordSnapshotSize(ctx, "pages", pagesImgPath, ns, tmpl) + if err := uploadIfExists(ctx, s.gcsClient, + prefix+"/pages.img.zstd", + pagesImgPath, + ); err != nil { + return nil, err + } + recordSnapshotSize(ctx, "pages_meta", pagesMetaImgPath, ns, tmpl) + if err := uploadIfExists(ctx, s.gcsClient, + prefix+"/pages_meta.img.zstd", + pagesMetaImgPath, + ); err != nil { + return nil, err + } } if err := resetActorDirs(ns, tmpl, actorID); err != nil { @@ -434,7 +520,7 @@ func (s *AteomHerder) Restore(ctx context.Context, req *ateletpb.RestoreRequest) return nil, status.Error(codes.InvalidArgument, err.Error()) } - runscPath, err := s.fetchRunscAndPrep(ctx, req.GetRunsc()) + runscPath, runtimeAssetPaths, err := s.fetchRuntimeForRequest(ctx, req.GetRunsc(), req.GetRuntimeAssets()) if err != nil { return nil, err } @@ -446,27 +532,34 @@ func (s *AteomHerder) Restore(ctx context.Context, req *ateletpb.RestoreRequest) } checkpointDir := ateompath.RestoreStateDir(req.GetActorTemplateNamespace(), req.GetActorTemplateName(), req.GetActorId()) - checkpointImgPath := filepath.Join(checkpointDir, "checkpoint.img") - pagesImgPath := filepath.Join(checkpointDir, "pages.img") - pagesMetaImgPath := filepath.Join(checkpointDir, "pages_meta.img") - prefix := strings.TrimSuffix(req.GetSnapshotUriPrefix(), "/") - g, gCtx := errgroup.WithContext(ctx) - for _, dl := range []struct{ remote, local string }{ - {prefix + "/checkpoint.img.zstd", checkpointImgPath}, - {prefix + "/pages.img.zstd", pagesImgPath}, - {prefix + "/pages_meta.img.zstd", pagesMetaImgPath}, - } { - dl := dl - g.Go(func() error { - if err := ategcs.FetchLocalFileFromGCSWithZstd(gCtx, s.gcsClient, dl.remote, dl.local); err != nil { - return fmt.Errorf("while downloading %s from GCS: %w", filepath.Base(dl.remote), err) - } - return nil - }) - } - if err := g.Wait(); err != nil { + + // Prefer a manifest-based snapshot (e.g. cloud-hypervisor); fall back to the + // legacy fixed file set (gVisor) when there is no manifest. + if ok, err := downloadSnapshotManifest(ctx, s.gcsClient, prefix, checkpointDir); err != nil { return nil, err + } else if !ok { + checkpointImgPath := filepath.Join(checkpointDir, "checkpoint.img") + pagesImgPath := filepath.Join(checkpointDir, "pages.img") + pagesMetaImgPath := filepath.Join(checkpointDir, "pages_meta.img") + + g, gCtx := errgroup.WithContext(ctx) + for _, dl := range []struct{ remote, local string }{ + {prefix + "/checkpoint.img.zstd", checkpointImgPath}, + {prefix + "/pages.img.zstd", pagesImgPath}, + {prefix + "/pages_meta.img.zstd", pagesMetaImgPath}, + } { + dl := dl + g.Go(func() error { + if err := ategcs.FetchLocalFileFromGCSWithZstd(gCtx, s.gcsClient, dl.remote, dl.local); err != nil { + return fmt.Errorf("while downloading %s from GCS: %w", filepath.Base(dl.remote), err) + } + return nil + }) + } + if err := g.Wait(); err != nil { + return nil, err + } } if err := s.prepareOCIBundles(ctx, ns, tmpl, actorID, @@ -487,6 +580,7 @@ func (s *AteomHerder) Restore(ctx context.Context, req *ateletpb.RestoreRequest) ActorTemplateName: tmpl, ActorId: actorID, RunscPath: runscPath, + RuntimeAssetPaths: runtimeAssetPaths, Spec: buildAteomWorkloadSpec(req.GetSpec()), }); err != nil { return nil, fmt.Errorf("while calling ateom.RestoreWorkload: %w", err) @@ -607,6 +701,80 @@ func uploadIfExists(ctx context.Context, gcs ategcs.ObjectStorage, remoteURI, lo return nil } +// snapshotManifestName is the object (under the snapshot prefix) that lists the +// files comprising a runtime-reported snapshot. Its presence distinguishes a +// manifest-based snapshot (e.g. cloud-hypervisor) from the legacy gVisor fixed +// file set. +const snapshotManifestName = "manifest.json" + +type snapshotManifest struct { + Files []string `json:"files"` +} + +// uploadSnapshotFiles ships a runtime-reported snapshot file set (each +// zstd-compressed) plus a manifest written last so its presence signals a +// complete upload. Used for runtimes that populate +// CheckpointWorkloadResponse.snapshot_files (e.g. cloud-hypervisor). +func uploadSnapshotFiles(ctx context.Context, gcs ategcs.ObjectStorage, prefix, dir string, files []string) error { + g, gCtx := errgroup.WithContext(ctx) + for _, f := range files { + f := f + g.Go(func() error { + if err := ategcs.SendLocalFileToGCSWithZstd(gCtx, gcs, prefix+"/"+f+".zstd", filepath.Join(dir, f)); err != nil { + return fmt.Errorf("while uploading %s: %w", f, err) + } + return nil + }) + } + if err := g.Wait(); err != nil { + return err + } + manifestPath := filepath.Join(dir, snapshotManifestName) + b, err := json.Marshal(snapshotManifest{Files: files}) + if err != nil { + return err + } + if err := os.WriteFile(manifestPath, b, 0o600); err != nil { + return err + } + if err := ategcs.SendLocalFileToGCSWithZstd(ctx, gcs, prefix+"/"+snapshotManifestName+".zstd", manifestPath); err != nil { + return fmt.Errorf("while uploading snapshot manifest: %w", err) + } + return nil +} + +// downloadSnapshotManifest fetches the manifest (if any) and every listed file +// into dir. Returns ok=false when no manifest exists, so the caller falls back +// to the legacy fixed file set (gVisor). +func downloadSnapshotManifest(ctx context.Context, gcs ategcs.ObjectStorage, prefix, dir string) (bool, error) { + manifestPath := filepath.Join(dir, snapshotManifestName) + if err := ategcs.FetchLocalFileFromGCSWithZstd(ctx, gcs, prefix+"/"+snapshotManifestName+".zstd", manifestPath); err != nil { + return false, nil // no manifest -> legacy snapshot + } + b, err := os.ReadFile(manifestPath) + if err != nil { + return false, err + } + var m snapshotManifest + if err := json.Unmarshal(b, &m); err != nil { + return false, fmt.Errorf("parsing snapshot manifest: %w", err) + } + g, gCtx := errgroup.WithContext(ctx) + for _, f := range m.Files { + f := f + g.Go(func() error { + if err := ategcs.FetchLocalFileFromGCSWithZstd(gCtx, gcs, prefix+"/"+f+".zstd", filepath.Join(dir, f)); err != nil { + return fmt.Errorf("while downloading %s: %w", f, err) + } + return nil + }) + } + if err := g.Wait(); err != nil { + return false, err + } + return true, nil +} + type AteomDialer struct { conns *lru.Cache } diff --git a/cmd/atelet/snapshot_manifest_test.go b/cmd/atelet/snapshot_manifest_test.go new file mode 100644 index 000000000..6905f9bee --- /dev/null +++ b/cmd/atelet/snapshot_manifest_test.go @@ -0,0 +1,115 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "path/filepath" + "sync" + "testing" +) + +// memStore is an in-memory ategcs.ObjectStorage for testing. +type memStore struct { + mu sync.Mutex + data map[string][]byte +} + +func (m *memStore) key(bucket, object string) string { return bucket + "/" + object } + +func (m *memStore) GetObject(ctx context.Context, bucket, object string) (io.ReadCloser, error) { + m.mu.Lock() + defer m.mu.Unlock() + b, ok := m.data[m.key(bucket, object)] + if !ok { + return nil, fmt.Errorf("object not found: %s/%s", bucket, object) + } + return io.NopCloser(bytes.NewReader(b)), nil +} + +func (m *memStore) PutObject(ctx context.Context, bucket, object string, r io.Reader) error { + b, err := io.ReadAll(r) + if err != nil { + return err + } + m.mu.Lock() + defer m.mu.Unlock() + if m.data == nil { + m.data = map[string][]byte{} + } + m.data[m.key(bucket, object)] = b + return nil +} + +// TestSnapshotManifestRoundTrip verifies the cloud-hypervisor snapshot ship path: +// uploadSnapshotFiles writes a manifest + each file, and downloadSnapshotManifest +// reconstructs them byte-for-byte at the destination. +func TestSnapshotManifestRoundTrip(t *testing.T) { + store := &memStore{data: map[string][]byte{}} + ctx := context.Background() + + src := t.TempDir() + files := []string{"config.json", "state.json", "memory-ranges", "shared-dir.tar"} + want := map[string][]byte{} + for i, f := range files { + content := []byte(fmt.Sprintf("contents-of-%s-%d\x00\x01\x02", f, i)) + if err := os.WriteFile(filepath.Join(src, f), content, 0o600); err != nil { + t.Fatal(err) + } + want[f] = content + } + + prefix := "gs://test-bucket/actors/x/snapshots/y" + if err := uploadSnapshotFiles(ctx, store, prefix, src, files); err != nil { + t.Fatalf("uploadSnapshotFiles: %v", err) + } + + dst := t.TempDir() + ok, err := downloadSnapshotManifest(ctx, store, prefix, dst) + if err != nil { + t.Fatalf("downloadSnapshotManifest: %v", err) + } + if !ok { + t.Fatal("downloadSnapshotManifest returned ok=false, want true") + } + for _, f := range files { + got, err := os.ReadFile(filepath.Join(dst, f)) + if err != nil { + t.Errorf("restored %q: %v", f, err) + continue + } + if !bytes.Equal(got, want[f]) { + t.Errorf("restored %q = %q, want %q", f, got, want[f]) + } + } +} + +// TestDownloadSnapshotManifestMissing verifies the legacy fallback: with no +// manifest in storage, downloadSnapshotManifest reports ok=false (no error) so +// the caller uses the fixed gVisor file set. +func TestDownloadSnapshotManifestMissing(t *testing.T) { + store := &memStore{data: map[string][]byte{}} + ok, err := downloadSnapshotManifest(context.Background(), store, "gs://test-bucket/none", t.TempDir()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if ok { + t.Error("ok = true for missing manifest, want false") + } +} diff --git a/cmd/ateom-cloud-hypervisor/internal/ch/api.go b/cmd/ateom-cloud-hypervisor/internal/ch/api.go new file mode 100644 index 000000000..97bf6832a --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/ch/api.go @@ -0,0 +1,130 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ch + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net" + "net/http" +) + +// apiBase is a placeholder host; the real transport always dials the unix +// api-socket, so the host portion of the URL is ignored. +const apiBase = "http://localhost" + +// apiClient speaks the cloud-hypervisor REST API over its unix api-socket. +// +// cloud-hypervisor serves an HTTP/1.1 REST API on the api-socket, and we drive snapshot/restore +// through it (vm.pause, vm.snapshot, vm.resume, vmm.ping, ...). +type apiClient struct { + http *http.Client +} + +func newAPIClient(socketPath string) *apiClient { + return &apiClient{ + http: &http.Client{ + Transport: &http.Transport{ + // CH's API server closes idle connections (and can get heavily + // swapped out during reclaim). Reusing a kept-alive connection + // then blocks forever on the next request (observed + // empirically: vm.resume hangs on a reused connection while + // a fresh one works instantly). Force a fresh connection per + // request. + DisableKeepAlives: true, + DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { + var d net.Dialer + return d.DialContext(ctx, "unix", socketPath) + }, + }, + }, + } +} + +// get issues a GET and checks for a 2xx status. +func (c *apiClient) get(ctx context.Context, path string) error { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiBase+path, nil) + if err != nil { + return err + } + resp, err := c.http.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + _, _ = io.Copy(io.Discard, resp.Body) + if resp.StatusCode >= 300 { + return fmt.Errorf("GET %s: status %d", path, resp.StatusCode) + } + return nil +} + +// getJSON issues a GET and decodes the 2xx JSON response into out. +func (c *apiClient) getJSON(ctx context.Context, path string, out any) error { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiBase+path, nil) + if err != nil { + return err + } + resp, err := c.http.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + b, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + if resp.StatusCode >= 300 { + return fmt.Errorf("GET %s: status %d: %s", path, resp.StatusCode, bytes.TrimSpace(b)) + } + return json.Unmarshal(b, out) +} + +// put issues a PUT with an optional JSON body and checks for a 2xx status. +func (c *apiClient) put(ctx context.Context, path string, body any) error { + var rdr io.Reader + if body != nil { + b, err := json.Marshal(body) + if err != nil { + return err + } + rdr = bytes.NewReader(b) + } + req, err := http.NewRequestWithContext(ctx, http.MethodPut, apiBase+path, rdr) + if err != nil { + return err + } + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + resp, err := c.http.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + msg, _ := io.ReadAll(resp.Body) + if resp.StatusCode >= 300 { + return fmt.Errorf("PUT %s: status %d: %s", path, resp.StatusCode, bytes.TrimSpace(msg)) + } + return nil +} + +// snapshotConfig is the body of /api/v1/vm.snapshot. +type snapshotConfig struct { + DestinationURL string `json:"destination_url"` +} diff --git a/cmd/ateom-cloud-hypervisor/internal/ch/ch.go b/cmd/ateom-cloud-hypervisor/internal/ch/ch.go new file mode 100644 index 000000000..29ba903af --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/ch/ch.go @@ -0,0 +1,189 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ch drives a single cloud-hypervisor instance over its REST +// api-socket: pause, snapshot, resume against a running VMM (e.g. the socket +// kata creates at /run/vc/vm//clh-api.sock), plus relaunching a fresh VMM +// from a snapshot directory for restore. +// +// This is the snapshot/restore half of the ateom-cloud-hypervisor model: kata +// owns RUN (boot the micro-VM + run the OCI container), and ateom drives the CH +// REST API underneath for suspend (pause+snapshot) and owns the bare-CH +// relaunch for restore. The REST wire format and the --restore CLI form are +// the ones cloud-hypervisor documents for snapshot/restore. +package ch + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "time" +) + +// Client talks to one cloud-hypervisor VMM over its unix api-socket. +type Client struct { + apiSocket string + api *apiClient +} + +// NewClient returns a Client bound to a cloud-hypervisor api-socket path. The +// socket need not exist yet; use WaitReady to block until the VMM answers. +func NewClient(apiSocket string) *Client { + return &Client{apiSocket: apiSocket, api: newAPIClient(apiSocket)} +} + +// APISocket returns the api-socket path this client is bound to. +func (c *Client) APISocket() string { return c.apiSocket } + +// Ping returns nil if the VMM api-socket answers vmm.ping. +func (c *Client) Ping(ctx context.Context) error { + return c.api.get(ctx, "/api/v1/vmm.ping") +} + +// WaitReady blocks until the api-socket answers vmm.ping or the deadline passes. +func (c *Client) WaitReady(ctx context.Context, deadline time.Duration) error { + end := time.Now().Add(deadline) + for { + if err := c.Ping(ctx); err == nil { + return nil + } + if !time.Now().Before(end) { + return fmt.Errorf("cloud-hypervisor api socket %q not ready after %s", c.apiSocket, deadline) + } + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(10 * time.Millisecond): + } + } +} + +// State returns the VM state as reported by vm.info (e.g. "Running", "Paused"). +func (c *Client) State(ctx context.Context) (string, error) { + var info struct { + State string `json:"state"` + } + if err := c.api.getJSON(ctx, "/api/v1/vm.info", &info); err != nil { + return "", err + } + return info.State, nil +} + +// Pause pauses the running guest (quiescing it before snapshot). Idempotent: +// already-paused is success (CH itself 500s on pausing a paused VM, which would +// otherwise wedge checkpoint retries after a partial earlier attempt). +func (c *Client) Pause(ctx context.Context) error { + if state, err := c.State(ctx); err == nil && state == "Paused" { + return nil + } + return c.api.put(ctx, "/api/v1/vm.pause", nil) +} + +// Resume resumes a paused guest (after snapshot or restore). +func (c *Client) Resume(ctx context.Context) error { + return c.api.put(ctx, "/api/v1/vm.resume", nil) +} + +// Snapshot writes the (paused) guest's state to destDir as a CH snapshot +// (config.json + state.json + memory-ranges). The guest must be paused first. +func (c *Client) Snapshot(ctx context.Context, destDir string) error { + if err := os.MkdirAll(destDir, 0o755); err != nil { + return fmt.Errorf("while creating snapshot dir %q: %w", destDir, err) + } + return c.api.put(ctx, "/api/v1/vm.snapshot", snapshotConfig{DestinationURL: SnapshotURL(destDir)}) +} + +// Shutdown best-effort tears down the VM and the VMM process behind the socket. +func (c *Client) Shutdown(ctx context.Context) error { + _ = c.api.put(ctx, "/api/v1/vm.shutdown", nil) + return c.api.put(ctx, "/api/v1/vmm.shutdown", nil) +} + +// SnapshotURL returns the file:// URL cloud-hypervisor expects for a snapshot +// destination or restore source directory. +func SnapshotURL(dir string) string { return "file://" + dir } + +// RestoreOptions configures relaunching a fresh cloud-hypervisor process from a +// snapshot directory (the restore half of suspend/resume). +type RestoreOptions struct { + // Binary is the cloud-hypervisor executable (defaults to "cloud-hypervisor" + // on PATH if empty). + Binary string + // APISocket is the api-socket path the new VMM should listen on. + APISocket string + // SourceDir is the snapshot directory to restore from. + SourceDir string + // MemoryRestoreMode selects how guest RAM is brought back: "" or "copy" for + // eager copy (CH default), "ondemand" for userfaultfd demand-paging. + MemoryRestoreMode string + // ExtraArgs are appended verbatim (e.g. cgroup/log flags). + ExtraArgs []string + // Stdout/Stderr capture the VMM process output (e.g. a vmm.log file). + Stdout io.Writer + Stderr io.Writer +} + +// RestoreArgs builds the cloud-hypervisor argv for restoring from a snapshot. +// Pure (no I/O) so it can be unit tested. +func RestoreArgs(o RestoreOptions) []string { + restoreArg := "source_url=" + SnapshotURL(o.SourceDir) + switch o.MemoryRestoreMode { + case "ondemand": + // ondemand uses userfaultfd; prefault must be off. + restoreArg += ",memory_restore_mode=ondemand,prefault=off" + case "", "copy": + // Eager copy is CH's default; leave memory_restore_mode unset. + } + args := []string{"--api-socket", o.APISocket, "--restore", restoreArg} + return append(args, o.ExtraArgs...) +} + +// Restore launches a fresh cloud-hypervisor process with --restore and waits +// until its api-socket answers. The restored VM comes back paused; call Resume +// on the returned Client to run it. The caller owns cmd (must Wait/Kill it). +func Restore(ctx context.Context, o RestoreOptions) (cmd *exec.Cmd, client *Client, err error) { + if o.APISocket == "" { + return nil, nil, fmt.Errorf("RestoreOptions.APISocket is required") + } + if o.SourceDir == "" { + return nil, nil, fmt.Errorf("RestoreOptions.SourceDir is required") + } + bin := o.Binary + if bin == "" { + bin = "cloud-hypervisor" + } + // A stale socket from a prior VMM blocks bind; remove it best-effort. + _ = os.Remove(o.APISocket) + + // Deliberately NOT exec.CommandContext: the restored VMM must outlive the + // Restore RPC whose ctx launched it (gRPC cancels the ctx when the handler + // returns, which would SIGKILL the freshly restored VM). The caller owns + // cmd; the WaitReady below honors ctx for bootstrap cancellation. + cmd = exec.Command(bin, RestoreArgs(o)...) + cmd.Stdout = o.Stdout + cmd.Stderr = o.Stderr + if err := cmd.Start(); err != nil { + return nil, nil, fmt.Errorf("while starting cloud-hypervisor --restore: %w", err) + } + + client = NewClient(o.APISocket) + if err := client.WaitReady(ctx, 15*time.Second); err != nil { + _ = cmd.Process.Kill() + _, _ = cmd.Process.Wait() + return nil, nil, fmt.Errorf("while waiting for restored VMM: %w", err) + } + return cmd, client, nil +} diff --git a/cmd/ateom-cloud-hypervisor/internal/ch/ch_test.go b/cmd/ateom-cloud-hypervisor/internal/ch/ch_test.go new file mode 100644 index 000000000..4e2da1d38 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/ch/ch_test.go @@ -0,0 +1,179 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ch + +import ( + "context" + "encoding/json" + "io" + "net" + "net/http" + "path/filepath" + "reflect" + "sync" + "testing" + "time" +) + +func TestSnapshotURL(t *testing.T) { + if got, want := SnapshotURL("/var/lib/snap"), "file:///var/lib/snap"; got != want { + t.Errorf("SnapshotURL = %q, want %q", got, want) + } +} + +func TestRestoreArgs(t *testing.T) { + tests := []struct { + name string + opts RestoreOptions + want []string + }{ + { + name: "default copy mode", + opts: RestoreOptions{APISocket: "/run/ch.sock", SourceDir: "/snap"}, + want: []string{"--api-socket", "/run/ch.sock", "--restore", "source_url=file:///snap"}, + }, + { + name: "explicit copy mode", + opts: RestoreOptions{APISocket: "/run/ch.sock", SourceDir: "/snap", MemoryRestoreMode: "copy"}, + want: []string{"--api-socket", "/run/ch.sock", "--restore", "source_url=file:///snap"}, + }, + { + name: "ondemand mode", + opts: RestoreOptions{APISocket: "/run/ch.sock", SourceDir: "/snap", MemoryRestoreMode: "ondemand"}, + want: []string{"--api-socket", "/run/ch.sock", "--restore", "source_url=file:///snap,memory_restore_mode=ondemand,prefault=off"}, + }, + { + name: "extra args appended", + opts: RestoreOptions{APISocket: "/run/ch.sock", SourceDir: "/snap", ExtraArgs: []string{"--log-file", "/tmp/vmm.log"}}, + want: []string{"--api-socket", "/run/ch.sock", "--restore", "source_url=file:///snap", "--log-file", "/tmp/vmm.log"}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := RestoreArgs(tc.opts); !reflect.DeepEqual(got, tc.want) { + t.Errorf("RestoreArgs() = %v, want %v", got, tc.want) + } + }) + } +} + +// fakeCH is a stand-in cloud-hypervisor REST server on a unix socket. It records +// the requests it receives so tests can assert on method/path/body. +type fakeCH struct { + mu sync.Mutex + requests []recordedReq + srv *http.Server +} + +type recordedReq struct { + method string + path string + body string +} + +func startFakeCH(t *testing.T) (*Client, *fakeCH) { + t.Helper() + sockPath := filepath.Join(t.TempDir(), "ch.sock") + lis, err := net.Listen("unix", sockPath) + if err != nil { + t.Fatalf("listen unix: %v", err) + } + + f := &fakeCH{} + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + f.mu.Lock() + f.requests = append(f.requests, recordedReq{method: r.Method, path: r.URL.Path, body: string(body)}) + f.mu.Unlock() + w.WriteHeader(http.StatusNoContent) + }) + f.srv = &http.Server{Handler: mux} + go f.srv.Serve(lis) + t.Cleanup(func() { _ = f.srv.Close() }) + + return NewClient(sockPath), f +} + +func (f *fakeCH) recorded() []recordedReq { + f.mu.Lock() + defer f.mu.Unlock() + out := make([]recordedReq, len(f.requests)) + copy(out, f.requests) + return out +} + +func TestClientLifecycleCalls(t *testing.T) { + client, fake := startFakeCH(t) + ctx := context.Background() + + if err := client.WaitReady(ctx, time.Second); err != nil { + t.Fatalf("WaitReady: %v", err) + } + if err := client.Pause(ctx); err != nil { + t.Fatalf("Pause: %v", err) + } + snapDir := filepath.Join(t.TempDir(), "snap") + if err := client.Snapshot(ctx, snapDir); err != nil { + t.Fatalf("Snapshot: %v", err) + } + if err := client.Resume(ctx); err != nil { + t.Fatalf("Resume: %v", err) + } + + reqs := fake.recorded() + // First request is the WaitReady ping; assert the meaningful ones after it. + var got []recordedReq + for _, r := range reqs { + if r.path == "/api/v1/vmm.ping" { + continue + } + got = append(got, r) + } + want := []recordedReq{ + // Pause checks vm.info first (idempotency); the fake's empty reply is an + // unparseable state, so Pause falls through to the actual vm.pause PUT. + {method: http.MethodGet, path: "/api/v1/vm.info", body: ""}, + {method: http.MethodPut, path: "/api/v1/vm.pause", body: ""}, + {method: http.MethodPut, path: "/api/v1/vm.snapshot"}, + {method: http.MethodPut, path: "/api/v1/vm.resume", body: ""}, + } + if len(got) != len(want) { + t.Fatalf("got %d non-ping requests %+v, want %d", len(got), got, len(want)) + } + for i := range want { + if got[i].method != want[i].method || got[i].path != want[i].path { + t.Errorf("request %d = %s %s, want %s %s", i, got[i].method, got[i].path, want[i].method, want[i].path) + } + } + + // The snapshot body must carry the file:// destination URL. + var snap snapshotConfig + if err := json.Unmarshal([]byte(got[2].body), &snap); err != nil { + t.Fatalf("snapshot body not JSON: %v (%q)", err, got[2].body) + } + if snap.DestinationURL != SnapshotURL(snapDir) { + t.Errorf("snapshot destination_url = %q, want %q", snap.DestinationURL, SnapshotURL(snapDir)) + } +} + +func TestWaitReadyTimesOut(t *testing.T) { + // Socket that never exists -> WaitReady should time out, not hang. + client := NewClient(filepath.Join(t.TempDir(), "nonexistent.sock")) + err := client.WaitReady(context.Background(), 50*time.Millisecond) + if err == nil { + t.Fatal("WaitReady returned nil for a dead socket, want timeout error") + } +} diff --git a/cmd/ateom-cloud-hypervisor/internal/ch/restorefds.go b/cmd/ateom-cloud-hypervisor/internal/ch/restorefds.go new file mode 100644 index 000000000..4f4767cf2 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/ch/restorefds.go @@ -0,0 +1,181 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ch + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "net" + "os" + "os/exec" + "strings" + "time" + + "golang.org/x/sys/unix" +) + +// RestoredNet identifies one fd-backed network device in a snapshot and the +// fresh tap FDs to back it with on restore. kata boots CH virtio-net devices +// from tap FDs, so the snapshot's config requires net_fds on restore +// (RestoreMissingRequiredNetId otherwise); CH reopens the device on the FDs we +// pass over the api-socket via SCM_RIGHTS. +type RestoredNet struct { + // ID is the device id from the snapshot's config.json (e.g. "_net1"). + ID string + // FDs are open tap fds (one per queue pair) for CH to adopt. + FDs []int +} + +// LaunchVMMOptions configures starting a bare VMM (no VM) for an FD-passing +// restore. +type LaunchVMMOptions struct { + // Binary is the cloud-hypervisor executable (defaults to "cloud-hypervisor"). + Binary string + // APISocket is the api-socket path the new VMM should listen on. + APISocket string + // Stdout/Stderr receive the VMM's output. + Stdout, Stderr interface{ Write([]byte) (int, error) } +} + +// LaunchVMM starts a cloud-hypervisor process with only an api-socket (no VM) +// and waits until it answers. Use Client.RestoreWithNetFDs to then restore a +// snapshot that has fd-backed net devices. The caller owns cmd. +func LaunchVMM(ctx context.Context, o LaunchVMMOptions) (*exec.Cmd, *Client, error) { + if o.APISocket == "" { + return nil, nil, fmt.Errorf("LaunchVMMOptions.APISocket is required") + } + bin := o.Binary + if bin == "" { + bin = "cloud-hypervisor" + } + _ = os.Remove(o.APISocket) + // Deliberately NOT exec.CommandContext: the VMM must outlive the RPC whose + // ctx launched it. The caller owns cmd; WaitReady honors ctx. + cmd := exec.Command(bin, "--api-socket", o.APISocket) + cmd.Stdout = o.Stdout + cmd.Stderr = o.Stderr + if err := cmd.Start(); err != nil { + return nil, nil, fmt.Errorf("while starting cloud-hypervisor: %w", err) + } + client := NewClient(o.APISocket) + if err := client.WaitReady(ctx, 15*time.Second); err != nil { + _ = cmd.Process.Kill() + _, _ = cmd.Process.Wait() + return nil, nil, fmt.Errorf("while waiting for VMM api-socket: %w", err) + } + return cmd, client, nil +} + +// RestoreWithNetFDs issues vm.restore for a snapshot dir, passing fresh tap FDs +// for the snapshot's fd-backed net devices via SCM_RIGHTS on the api-socket +// (the only way CH accepts net FDs on restore; mirrors ch-remote's +// send_with_fds). The VM comes back paused; call Resume after. +func (c *Client) RestoreWithNetFDs(ctx context.Context, sourceDir string, nets []RestoredNet) error { + type restoredNetConfig struct { + ID string `json:"id"` + NumFDs int `json:"num_fds"` + } + cfg := struct { + SourceURL string `json:"source_url"` + NetFDs []restoredNetConfig `json:"net_fds,omitempty"` + }{SourceURL: SnapshotURL(sourceDir)} + var fds []int + for _, n := range nets { + cfg.NetFDs = append(cfg.NetFDs, restoredNetConfig{ID: n.ID, NumFDs: len(n.FDs)}) + fds = append(fds, n.FDs...) + } + body, err := json.Marshal(cfg) + if err != nil { + return err + } + + raddr, err := net.ResolveUnixAddr("unix", c.apiSocket) + if err != nil { + return err + } + conn, err := net.DialUnix("unix", nil, raddr) + if err != nil { + return fmt.Errorf("dialing api-socket: %w", err) + } + defer conn.Close() + if dl, ok := ctx.Deadline(); ok { + _ = conn.SetDeadline(dl) + } else { + _ = conn.SetDeadline(time.Now().Add(60 * time.Second)) + } + + // Raw HTTP/1.1 over the unix socket: net/http cannot attach SCM_RIGHTS, and + // CH's micro_http collects fds from the recvmsg ancillary data of the + // request that carries them. + req := fmt.Sprintf("PUT /api/v1/vm.restore HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n%s", len(body), body) + var oob []byte + if len(fds) > 0 { + oob = unix.UnixRights(fds...) + } + if _, _, err := conn.WriteMsgUnix([]byte(req), oob, nil); err != nil { + return fmt.Errorf("sending vm.restore with fds: %w", err) + } + + status, err := bufio.NewReader(conn).ReadString('\n') + if err != nil { + return fmt.Errorf("reading vm.restore response: %w", err) + } + parts := strings.SplitN(strings.TrimSpace(status), " ", 3) + if len(parts) < 2 || !strings.HasPrefix(parts[1], "2") { + return fmt.Errorf("vm.restore failed: %s", strings.TrimSpace(status)) + } + return nil +} + +// SnapshotNetDevice describes one net device found in a CH snapshot's +// config.json. Restore must supply net_fds for every one of them. +type SnapshotNetDevice struct { + // ID is the CH device id (e.g. "_net1"). + ID string + // QueuePairs is the number of tap FDs the device needs (num_queues/2). + QueuePairs int + // MAC is the guest-visible MAC address of the device. + MAC string +} + +// SnapshotNetDevices parses a CH snapshot's config.json and returns its net +// devices, in order. +func SnapshotNetDevices(snapshotDir string) ([]SnapshotNetDevice, error) { + b, err := os.ReadFile(snapshotDir + "/config.json") + if err != nil { + return nil, err + } + var cfg struct { + Net []struct { + ID string `json:"id"` + NumQueues int `json:"num_queues"` + MAC string `json:"mac"` + } `json:"net"` + } + if err := json.Unmarshal(b, &cfg); err != nil { + return nil, fmt.Errorf("parsing snapshot config.json: %w", err) + } + var out []SnapshotNetDevice + for _, n := range cfg.Net { + qp := n.NumQueues / 2 + if qp < 1 { + qp = 1 + } + out = append(out, SnapshotNetDevice{ID: n.ID, QueuePairs: qp, MAC: n.MAC}) + } + return out, nil +} diff --git a/cmd/ateom-cloud-hypervisor/internal/kata/checkpoint_integration_test.go b/cmd/ateom-cloud-hypervisor/internal/kata/checkpoint_integration_test.go new file mode 100644 index 000000000..8380bffb1 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/kata/checkpoint_integration_test.go @@ -0,0 +1,179 @@ +//go:build linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kata + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/agent-substrate/substrate/cmd/ateom-cloud-hypervisor/internal/ch" + "golang.org/x/sys/unix" +) + +// TestKataCheckpoint boots a busybox container in a CH micro-VM via the kata +// shim, then exercises the checkpoint path: drive CH's REST api-socket (the +// one kata created) to pause + snapshot, and verify a portable, sparse snapshot +// directory is produced. Gated behind KATA_INTEGRATION=1 (see kata_integration_test.go). +func TestKataCheckpoint(t *testing.T) { + if os.Getenv("KATA_INTEGRATION") != "1" { + t.Skip("set KATA_INTEGRATION=1 to run (requires kata + /dev/kvm)") + } + rootfsSrc := os.Getenv("KATA_ROOTFS_SRC") + if rootfsSrc == "" { + t.Fatal("KATA_ROOTFS_SRC is required") + } + shim := os.Getenv("KATA_SHIM") + if shim == "" { + shim = "/opt/kata/bin/containerd-shim-kata-v2" + } + + id := fmt.Sprintf("ateomchv-ckpt-%d", os.Getpid()) + work := filepath.Join("/tmp", id) + bundle := filepath.Join(work, "bundle") + rootfs := filepath.Join(bundle, "rootfs") + if err := os.MkdirAll(rootfs, 0o755); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = os.RemoveAll(work) }) + if out, err := exec.Command("cp", "-a", rootfsSrc+"/.", rootfs+"/").CombinedOutput(); err != nil { + t.Fatalf("copying rootfs: %v: %s", err, out) + } + writeBundleConfig(t, bundle, id) + drainLogFifo(t, bundle) + + s := &Shim{ + Binary: shim, + ID: id, + Bundle: bundle, + Namespace: "default", + GRPCAddress: filepath.Join(work, "fake-containerd.sock"), + TTRPCAddress: filepath.Join(work, "fake-containerd.sock.ttrpc"), + Diagnostics: testWriter{t}, + Debug: true, + } + + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + defer func() { + _ = s.Kill(ctx, 9, true) + sc, c := context.WithTimeout(context.Background(), 15*time.Second) + _ = s.Shutdown(sc, true) + c() + _ = s.Close() + cc, c2 := context.WithTimeout(context.Background(), 15*time.Second) + _ = s.CleanupAction(cc) + c2() + }() + + if err := s.Bootstrap(ctx); err != nil { + t.Fatalf("Bootstrap: %v", err) + } + if _, err := s.Create(ctx, CreateOptions{}); err != nil { + t.Fatalf("Create: %v", err) + } + if _, err := s.Start(ctx); err != nil { + t.Fatalf("Start: %v", err) + } + t.Log("micro-VM running; checkpointing via CH REST api-socket") + + // --- M2: drive the CH api-socket kata created. --- + client := ch.NewClient(CLHSocketPath(id)) + if err := client.WaitReady(ctx, 10*time.Second); err != nil { + t.Fatalf("CH WaitReady: %v", err) + } + if err := client.Pause(ctx); err != nil { + t.Fatalf("CH Pause: %v", err) + } + snapDir := filepath.Join(work, "snapshot") + if err := client.Snapshot(ctx, snapDir); err != nil { + t.Fatalf("CH Snapshot: %v", err) + } + t.Logf("snapshot written to %s", snapDir) + + // Verify the snapshot dir: config.json + state.json + a sparse memory file. + for _, f := range []string{"config.json", "state.json"} { + if _, err := os.Stat(filepath.Join(snapDir, f)); err != nil { + t.Errorf("expected snapshot file %q: %v", f, err) + } + } + entries, err := os.ReadDir(snapDir) + if err != nil { + t.Fatalf("reading snapshot dir: %v", err) + } + var memFile string + for _, e := range entries { + t.Logf("snapshot entry: %s", e.Name()) + if e.Name() != "config.json" && e.Name() != "state.json" { + memFile = e.Name() + } + } + if memFile == "" { + t.Fatal("no memory file in snapshot dir") + } + apparent, actual := fileSizes(t, filepath.Join(snapDir, memFile)) + t.Logf("memory file %q: apparent=%d bytes, actual(on-disk)=%d bytes", memFile, apparent, actual) + if actual >= apparent { + t.Errorf("memory file not sparse: actual %d >= apparent %d (need shared=on / sparse snapshot)", actual, apparent) + } + if apparent == 0 { + t.Errorf("memory file is empty") + } + t.Log("checkpoint produced a portable, sparse snapshot") +} + +// drainLogFifo creates the bundle "log" fifo the kata shim writes to and drains +// it to t.Log so the shim's diagnostics are visible (and it doesn't block). +func drainLogFifo(t *testing.T, bundle string) { + t.Helper() + logFifo := filepath.Join(bundle, "log") + if err := unix.Mkfifo(logFifo, 0o700); err != nil { + t.Fatalf("mkfifo log: %v", err) + } + go func() { + f, err := os.OpenFile(logFifo, os.O_RDONLY, 0) + if err != nil { + return + } + defer f.Close() + buf := make([]byte, 4096) + for { + n, err := f.Read(buf) + if n > 0 { + t.Logf("[shim-log] %s", buf[:n]) + } + if err != nil { + return + } + } + }() +} + +// fileSizes returns a file's apparent size and its actual on-disk size (from +// st_blocks), so a caller can detect sparseness. +func fileSizes(t *testing.T, path string) (apparent, actual int64) { + t.Helper() + var st unix.Stat_t + if err := unix.Stat(path, &st); err != nil { + t.Fatalf("stat %q: %v", path, err) + } + return st.Size, st.Blocks * 512 +} diff --git a/cmd/ateom-cloud-hypervisor/internal/kata/cleanup_linux.go b/cmd/ateom-cloud-hypervisor/internal/kata/cleanup_linux.go new file mode 100644 index 000000000..10ed0a321 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/kata/cleanup_linux.go @@ -0,0 +1,91 @@ +//go:build linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kata + +import ( + "os" + "path/filepath" + "sort" + "strconv" + "strings" + + "golang.org/x/sys/unix" +) + +// CleanupSandboxState removes kata's leftover host-side state for a sandbox id +// (the shared sandbox dir, the per-VM runtime dir, and the sbs dir), lazily +// unmounting anything still mounted underneath them first, and kills orphaned +// per-sandbox processes. Because ateom drives the shim directly (no +// containerd), a failed Create does not fully self-clean; the deterministic +// sandbox id (= actor id) then collides on the next attempt: "listen unix +// .../virtiofsd.sock: bind: address already in use", "Could not bind mount +// .../shared/sandboxes//mounts", "directory not empty". Calling this +// before each run gives kata a clean slate. Safe when nothing exists. +func CleanupSandboxState(id string) { + dirs := []string{ + filepath.Join("/run/kata-containers/shared/sandboxes", id), + filepath.Join(vcVMDir, id), + filepath.Join("/run/vc/sbs", id), + } + if b, err := os.ReadFile("/proc/self/mountinfo"); err == nil { + var mounts []string + for _, line := range strings.Split(string(b), "\n") { + fields := strings.Fields(line) + if len(fields) < 5 { + continue + } + mp := fields[4] // mount point + for _, d := range dirs { + if mp == d || strings.HasPrefix(mp, d+"/") { + mounts = append(mounts, mp) + break + } + } + } + // Deepest paths first so child mounts unmount before their parents. + sort.Slice(mounts, func(i, j int) bool { return len(mounts[i]) > len(mounts[j]) }) + for _, mp := range mounts { + _ = unix.Unmount(mp, unix.MNT_DETACH) + } + } + for _, d := range dirs { + _ = os.RemoveAll(d) + } + // Kill orphaned per-sandbox processes (cloud-hypervisor / virtiofsd / shim) + // left by a prior killed attempt: a canceled Create leaves the CH it spawned + // running (reparented to us) holding guest RAM and stale sockets. Matched + // strictly by the sandbox id (an actor UUID) appearing in the cmdline, so + // nothing unrelated can match. + entries, err := os.ReadDir("/proc") + if err != nil { + return + } + for _, e := range entries { + pid, perr := strconv.Atoi(e.Name()) + if perr != nil || pid == os.Getpid() { + continue + } + cmdline, rerr := os.ReadFile(filepath.Join("/proc", e.Name(), "cmdline")) + if rerr != nil || !strings.Contains(string(cmdline), id) { + continue + } + argv0 := strings.SplitN(string(cmdline), "\x00", 2)[0] + if strings.Contains(argv0, "cloud-hypervisor") || strings.Contains(argv0, "virtiofsd") || strings.Contains(argv0, "containerd-shim-kata") { + _ = unix.Kill(pid, unix.SIGKILL) + } + } +} diff --git a/cmd/ateom-cloud-hypervisor/internal/kata/config.go b/cmd/ateom-cloud-hypervisor/internal/kata/config.go new file mode 100644 index 000000000..d1ac84e49 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/kata/config.go @@ -0,0 +1,114 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kata + +import ( + "fmt" + "regexp" +) + +// ConfigAssets are the runtime-fetched asset paths to splice into a kata +// configuration.toml so the worker image needs no baked /opt/kata. Each is an +// absolute on-node path (content-addressed under the static-files dir, like runsc). +type ConfigAssets struct { + // Kernel is the guest kernel (vmlinux) path. + Kernel string + // Image is the guest OS rootfs image path. + Image string + // Hypervisor is the cloud-hypervisor binary path. + Hypervisor string + // Virtiofsd is the virtiofsd binary path (needs find-paths migration + // support, >= 1.13; kata bundles an older build without it). + Virtiofsd string +} + +// configField is a single TOML key whose value we rewrite to a fetched path. +type configField struct { + key string + value func(ConfigAssets) string +} + +// pathFields are the asset-path keys in a clh configuration.toml. We rewrite +// each (and its valid_* allowlist) to the fetched path. Empirically validated: +// a stock config with only these lines rewritten boots a VM via KATA_CONF_FILE. +var pathFields = []configField{ + {"kernel", func(a ConfigAssets) string { return quote(a.Kernel) }}, + {"image", func(a ConfigAssets) string { return quote(a.Image) }}, + {"path", func(a ConfigAssets) string { return quote(a.Hypervisor) }}, + {"valid_hypervisor_paths", func(a ConfigAssets) string { return list(a.Hypervisor) }}, + {"virtio_fs_daemon", func(a ConfigAssets) string { return quote(a.Virtiofsd) }}, + {"valid_virtio_fs_daemon_paths", func(a ConfigAssets) string { return list(a.Virtiofsd) }}, +} + +func quote(s string) string { return `"` + s + `"` } +func list(s string) string { return `["` + s + `"]` } + +// EnableDebug turns on kata's debug knobs in a configuration.toml: it uncomments +// every `#enable_debug = true` (hypervisor/agent/runtime) and appends +// `agent.log=debug` to the hypervisor kernel_params so the guest kata-agent emits +// debug-level logs (with the failing path on errors) over its vsock log channel, +// which the shim relays to our log fifo -> pod logs. POC diagnostic aid. +func EnableDebug(base []byte) []byte { + out := base + // Uncomment all `#enable_debug = true` lines. + reDbg := regexp.MustCompile(`(?m)^(\s*)#\s*enable_debug\s*=\s*true\s*$`) + out = reDbg.ReplaceAll(out, []byte("${1}enable_debug = true")) + // Append agent.log=debug to kernel_params (only if not already present). + reKP := regexp.MustCompile(`(?m)^(\s*kernel_params\s*=\s*")([^"]*)(".*)$`) + out = reKP.ReplaceAllFunc(out, func(line []byte) []byte { + m := reKP.FindSubmatch(line) + if m == nil { + return line + } + existing := string(m[2]) + if regexpContains(existing, "agent.log=") { + return line + } + val := "agent.log=debug agent.debug_console" + if existing != "" { + val = existing + " " + val + } + return []byte(string(m[1]) + val + string(m[3])) + }) + return out +} + +func regexpContains(s, sub string) bool { + return regexp.MustCompile(regexp.QuoteMeta(sub)).MatchString(s) +} + +// RenderConfig returns base (a kata configuration.toml) with the asset-path +// fields rewritten to point at a.* . The base config carries all the +// version-matched kata settings; we only override where the assets live, so the +// config stays in sync with the kata release (the base itself is a fetched asset). +// +// Each field must already be present in base (kata's stock clh config has them); +// a missing field is an error so we fail loudly rather than boot a half-configured VM. +func RenderConfig(base []byte, a ConfigAssets) ([]byte, error) { + if a.Kernel == "" || a.Image == "" || a.Hypervisor == "" || a.Virtiofsd == "" { + return nil, fmt.Errorf("RenderConfig: all of Kernel/Image/Hypervisor/Virtiofsd are required, got %+v", a) + } + out := base + for _, f := range pathFields { + // Match a top-level `key = ` line (TOML), preserving leading + // whitespace. Anchored to line start in multiline mode. + re := regexp.MustCompile(`(?m)^(\s*` + regexp.QuoteMeta(f.key) + `\s*=\s*).*$`) + if !re.Match(out) { + return nil, fmt.Errorf("RenderConfig: key %q not found in base config", f.key) + } + out = re.ReplaceAll(out, []byte("${1}"+f.value(a))) + } + return out, nil +} diff --git a/cmd/ateom-cloud-hypervisor/internal/kata/config_test.go b/cmd/ateom-cloud-hypervisor/internal/kata/config_test.go new file mode 100644 index 000000000..002e9466e --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/kata/config_test.go @@ -0,0 +1,91 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kata + +import ( + "strings" + "testing" +) + +// stockConfig mirrors the asset-path lines of kata's clh configuration.toml +// (the rest of the file is elided; RenderConfig only touches these lines). +const stockConfig = `[hypervisor.clh] +path = "/usr/local/bin/cloud-hypervisor" +kernel = "/opt/kata/share/kata-containers/vmlinux.container" +image = "/opt/kata/share/kata-containers/kata-containers.img" +valid_hypervisor_paths = ["/opt/kata/bin/cloud-hypervisor","/usr/local/bin/cloud-hypervisor"] +shared_fs = "virtio-fs" +virtio_fs_daemon = "/usr/local/bin/virtiofsd-patched" +valid_virtio_fs_daemon_paths = ["/usr/local/bin/virtiofsd-patched","/opt/kata/libexec/virtiofsd"] +` + +func TestRenderConfig(t *testing.T) { + a := ConfigAssets{ + Kernel: "/var/lib/ateom-gvisor/static-files/vmlinux-abc", + Image: "/var/lib/ateom-gvisor/static-files/rootfs-def", + Hypervisor: "/var/lib/ateom-gvisor/static-files/cloud-hypervisor-123", + Virtiofsd: "/var/lib/ateom-gvisor/static-files/virtiofsd-456", + } + out, err := RenderConfig([]byte(stockConfig), a) + if err != nil { + t.Fatalf("RenderConfig: %v", err) + } + got := string(out) + + wantLines := []string{ + `kernel = "` + a.Kernel + `"`, + `image = "` + a.Image + `"`, + `path = "` + a.Hypervisor + `"`, + `valid_hypervisor_paths = ["` + a.Hypervisor + `"]`, + `virtio_fs_daemon = "` + a.Virtiofsd + `"`, + `valid_virtio_fs_daemon_paths = ["` + a.Virtiofsd + `"]`, + } + for _, w := range wantLines { + if !strings.Contains(got, w) { + t.Errorf("rendered config missing line %q\n--- got ---\n%s", w, got) + } + } + // Untouched settings must survive. + if !strings.Contains(got, `shared_fs = "virtio-fs"`) { + t.Error("RenderConfig dropped shared_fs") + } + // No stale /opt/kata or default cloud-hypervisor paths remain. + for _, stale := range []string{"/opt/kata/share", "/opt/kata/bin/cloud-hypervisor", "/opt/kata/libexec/virtiofsd"} { + if strings.Contains(got, stale) { + t.Errorf("rendered config still references stale path %q", stale) + } + } +} + +func TestRenderConfigMissingField(t *testing.T) { + // Base lacking virtio_fs_daemon -> error (fail loudly). + base := `kernel = "/k" +image = "/i" +path = "/p" +valid_hypervisor_paths = ["/p"] +valid_virtio_fs_daemon_paths = ["/v"] +` + _, err := RenderConfig([]byte(base), ConfigAssets{Kernel: "/k2", Image: "/i2", Hypervisor: "/p2", Virtiofsd: "/v2"}) + if err == nil { + t.Fatal("expected error for missing virtio_fs_daemon field") + } +} + +func TestRenderConfigMissingAsset(t *testing.T) { + _, err := RenderConfig([]byte(stockConfig), ConfigAssets{Kernel: "/k"}) // others empty + if err == nil { + t.Fatal("expected error for missing asset paths") + } +} diff --git a/cmd/ateom-cloud-hypervisor/internal/kata/kata.go b/cmd/ateom-cloud-hypervisor/internal/kata/kata.go new file mode 100644 index 000000000..c2d50ad23 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/kata/kata.go @@ -0,0 +1,357 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package kata drives a single containerd-shim-kata-v2 instance over ttrpc +// WITHOUT a containerd daemon. +// +// The shim uses containerd's standard runtime-v2 framework: invoking it with +// the "start" action (cwd = the OCI bundle) brings up a per-task ttrpc server +// and prints its socket address on stdout. We then speak the task v2 API +// (Create/Start/Pause/Resume/Kill/Delete/...) directly against that socket. +// +// This is the RUN half of ateom-cloud-hypervisor: kata boots the cloud- +// hypervisor micro-VM (guest kernel + rootfs + kata-agent) and runs the OCI +// container; ateom drives CH's snapshot/restore underneath (see internal/ch), +// against the api-socket kata exposes at CLHSocketPath(id). +// +// Empirically verified against kata 3.31.0 (containerd-shim-kata-v2): the +// "start" action returns "unix:///run/containerd/s/" and works with no +// containerd running. +package kata + +import ( + "context" + "fmt" + "io" + "net" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + task "github.com/containerd/containerd/api/runtime/task/v2" + "github.com/containerd/containerd/api/types" + "github.com/containerd/ttrpc" + "golang.org/x/sys/unix" +) + +const ( + // DefaultShimBinary is the kata containerd shim v2 executable. + DefaultShimBinary = "containerd-shim-kata-v2" + + // RuntimeID is the kata shim's containerd runtime identifier. + RuntimeID = "io.containerd.kata.v2" + + // vcVMDir is where kata creates per-sandbox runtime state, including the + // cloud-hypervisor API socket. + vcVMDir = "/run/vc/vm" +) + +// CLHSocketPath returns the cloud-hypervisor API socket kata creates for the +// sandbox with the given id. ateom drives CH snapshot/restore against it. +func CLHSocketPath(id string) string { + return filepath.Join(vcVMDir, id, "clh-api.sock") +} + +// Shim drives one containerd-shim-kata-v2 over ttrpc. +type Shim struct { + // Binary is the shim executable; defaults to DefaultShimBinary on PATH. + Binary string + // ID is the container/sandbox id. It also names the per-sandbox runtime + // dir, so CLHSocketPath(ID) locates the CH api-socket. + ID string + // Bundle is the OCI bundle directory (contains config.json and rootfs/). + Bundle string + // Namespace is the containerd namespace the shim runs under (e.g. "default"). + Namespace string + // GRPCAddress is the containerd grpc address the shim records. We run no + // containerd, but the shim uses it (with Namespace + ID) to derive its + // deterministic ttrpc socket path, so it must be a stable unique path. + GRPCAddress string + // TTRPCAddress is the events-publish target. With no containerd to receive + // them, kata logs publish failures but continues; point it anywhere stable. + TTRPCAddress string + // Diagnostics receives the start/delete action's stdout/stderr if set. + Diagnostics io.Writer + // Debug enables the shim's -debug flag (verbose logs to the bundle "log" fifo). + Debug bool + // ConfigFile, if set, is passed to the shim as KATA_CONF_FILE so it uses a + // generated configuration.toml (pointing at runtime-fetched assets) instead + // of the well-known /etc/kata-containers path. + ConfigFile string + + addr string + conn net.Conn + client *ttrpc.Client + task task.TTRPCTaskService + cmd *exec.Cmd // the foreground shim server process we manage +} + +func (s *Shim) binary() string { + if s.Binary != "" { + return s.Binary + } + return DefaultShimBinary +} + +func (s *Shim) shimEnv() []string { + env := append(os.Environ(), + "TTRPC_ADDRESS="+s.TTRPCAddress, + "GRPC_ADDRESS="+s.GRPCAddress, + "NAMESPACE="+s.Namespace, + ) + if s.ConfigFile != "" { + env = append(env, "KATA_CONF_FILE="+s.ConfigFile) + } + return env +} + +// Bootstrap launches the shim's ttrpc server in the FOREGROUND (on an explicit +// -socket) and connects to it. After Bootstrap the shim is up but no VM exists +// yet; call Create to boot the micro-VM and create the container. +// +// We deliberately do NOT use the containerd "start" action: it double-forks a +// detached daemon that dies silently in a minimal pod (PID 1 = ateom, no +// containerd). Running the server in the foreground as a child we manage is the +// path that works in-pod, and lets ateom own its lifecycle. +func (s *Shim) Bootstrap(ctx context.Context) error { + if s.ID == "" || s.Bundle == "" { + return fmt.Errorf("kata.Shim requires ID and Bundle") + } + + // The socket dir is containerd convention; create it (no containerd does). + if err := os.MkdirAll(shimSocketDir, 0o711); err != nil { + return fmt.Errorf("while creating shim socket dir %q: %w", shimSocketDir, err) + } + socketPath := filepath.Join(shimSocketDir, "ateomchv-"+s.ID+".sock") + _ = os.Remove(socketPath) + + // The shim opens /log (O_WRONLY) for its logrus output and blocks + // until a reader appears; create the fifo and drain it, or the server hangs + // during logging setup and never binds its socket. + if err := s.startLogDrain(); err != nil { + return err + } + + args := []string{ + "-namespace", s.Namespace, + "-address", s.GRPCAddress, + "-id", s.ID, + } + if s.Debug { + args = append(args, "-debug") + } + args = append(args, "-socket", socketPath) + + // Deliberately NOT exec.CommandContext: the shim must outlive the + // RunWorkload RPC whose ctx spawned it (gRPC cancels the ctx when the + // handler returns, and CommandContext would then SIGKILL the shim under a + // healthy running actor). Lifetime is managed via s.cmd (Close kills it); + // the dial loop below honors ctx for bootstrap cancellation. + cmd := exec.Command(s.binary(), args...) + cmd.Dir = s.Bundle + cmd.Env = s.shimEnv() + cmd.Stdout = s.Diagnostics + cmd.Stderr = s.Diagnostics + if err := cmd.Start(); err != nil { + return fmt.Errorf("while starting shim server: %w", err) + } + s.cmd = cmd + s.addr = socketPath + + // Wait for the server to bind + connect. + deadline := time.Now().Add(15 * time.Second) + var conn net.Conn + var err error + for { + conn, err = net.DialTimeout("unix", socketPath, 2*time.Second) + if err == nil { + break + } + if !time.Now().Before(deadline) { + _ = cmd.Process.Kill() + return fmt.Errorf("while dialing shim ttrpc socket %q: %w", socketPath, err) + } + select { + case <-ctx.Done(): + _ = cmd.Process.Kill() + return ctx.Err() + case <-time.After(50 * time.Millisecond): + } + } + s.conn = conn + s.client = ttrpc.NewClient(conn) + s.task = task.NewTTRPCTaskClient(s.client) + return nil +} + +// shimSocketDir is where the kata shim binds its ttrpc socket (containerd +// convention: /run/containerd/s/). +const shimSocketDir = "/run/containerd/s" + +// startLogDrain creates the /log fifo the shim writes its logs to and +// drains it (to Diagnostics, else discarded). The shim opens it O_WRONLY and +// blocks until a reader exists, so this must run before launching the server. +func (s *Shim) startLogDrain() error { + logFifo := filepath.Join(s.Bundle, "log") + _ = os.Remove(logFifo) + if err := unix.Mkfifo(logFifo, 0o700); err != nil { + return fmt.Errorf("while creating shim log fifo: %w", err) + } + go func() { + f, err := os.OpenFile(logFifo, os.O_RDONLY, 0) + if err != nil { + return + } + defer f.Close() + dst := io.Writer(io.Discard) + if s.Diagnostics != nil { + dst = s.Diagnostics + } + _, _ = io.Copy(dst, f) + }() + return nil +} + +// Address returns the shim's ttrpc socket path (after Bootstrap). +func (s *Shim) Address() string { return s.addr } + +// CreateOptions configures the container/task creation. +type CreateOptions struct { + // Rootfs are mounts the shim should set up at /rootfs. Leave nil + // when the bundle's rootfs/ is already populated (atelet's model). + Rootfs []*types.Mount + // Stdin/Stdout/Stderr are fifo or file paths for the container's stdio. + // Empty discards. + Stdin string + Stdout string + Stderr string + // Terminal requests a pty for the container's process. + Terminal bool +} + +// Create boots the micro-VM and creates the container from the bundle. Returns +// the task pid (the in-VMM process as seen by the shim). +func (s *Shim) Create(ctx context.Context, o CreateOptions) (uint32, error) { + resp, err := s.task.Create(ctx, &task.CreateTaskRequest{ + ID: s.ID, + Bundle: s.Bundle, + Rootfs: o.Rootfs, + Terminal: o.Terminal, + Stdin: o.Stdin, + Stdout: o.Stdout, + Stderr: o.Stderr, + }) + if err != nil { + return 0, fmt.Errorf("while calling task.Create: %w", err) + } + return resp.GetPid(), nil +} + +// Start starts the created container's init process. +func (s *Shim) Start(ctx context.Context) (uint32, error) { + resp, err := s.task.Start(ctx, &task.StartRequest{ID: s.ID}) + if err != nil { + return 0, fmt.Errorf("while calling task.Start: %w", err) + } + return resp.GetPid(), nil +} + +// Pause pauses the container (quiesce before snapshot). +func (s *Shim) Pause(ctx context.Context) error { + if _, err := s.task.Pause(ctx, &task.PauseRequest{ID: s.ID}); err != nil { + return fmt.Errorf("while calling task.Pause: %w", err) + } + return nil +} + +// Resume resumes a paused container. +func (s *Shim) Resume(ctx context.Context) error { + if _, err := s.task.Resume(ctx, &task.ResumeRequest{ID: s.ID}); err != nil { + return fmt.Errorf("while calling task.Resume: %w", err) + } + return nil +} + +// Kill sends a signal to the container. If all is true, signals every process. +func (s *Shim) Kill(ctx context.Context, signal uint32, all bool) error { + if _, err := s.task.Kill(ctx, &task.KillRequest{ID: s.ID, Signal: signal, All: all}); err != nil { + return fmt.Errorf("while calling task.Kill: %w", err) + } + return nil +} + +// Delete deletes the container/task (after it has stopped). +func (s *Shim) Delete(ctx context.Context) error { + if _, err := s.task.Delete(ctx, &task.DeleteRequest{ID: s.ID}); err != nil { + return fmt.Errorf("while calling task.Delete: %w", err) + } + return nil +} + +// Shutdown stops the shim (and its VMM). Pass now=true to force. +func (s *Shim) Shutdown(ctx context.Context, now bool) error { + if s.task == nil { + return nil + } + if _, err := s.task.Shutdown(ctx, &task.ShutdownRequest{ID: s.ID, Now: now}); err != nil { + return fmt.Errorf("while calling task.Shutdown: %w", err) + } + return nil +} + +// Close closes the ttrpc connection and stops the foreground shim server we +// launched (which also tears down its VMM). +func (s *Shim) Close() error { + var firstErr error + if s.client != nil { + if err := s.client.Close(); err != nil { + firstErr = err + } + } + if s.cmd != nil && s.cmd.Process != nil { + _ = s.cmd.Process.Kill() + _, _ = s.cmd.Process.Wait() + } + return firstErr +} + +// CleanupAction runs the shim's "delete" action, which tears down any leftover +// sandbox state for ID. Best-effort; safe to call after Shutdown. +// +// After a successful Shutdown the per-sandbox state dir is already gone, so the +// "delete" action reports "/run/vc/sbs/: no such file or directory" and +// exits non-zero; that case is treated as success (nothing left to clean). +func (s *Shim) CleanupAction(ctx context.Context) error { + cmd := exec.CommandContext(ctx, s.binary(), + "-namespace", s.Namespace, + "-address", s.GRPCAddress, + "-id", s.ID, + "delete", + ) + cmd.Dir = s.Bundle + cmd.Env = s.shimEnv() + out, err := cmd.CombinedOutput() + if s.Diagnostics != nil { + fmt.Fprintf(s.Diagnostics, "shim delete: %q (err=%v)\n", string(out), err) + } + if err != nil { + if strings.Contains(string(out), "no such file or directory") { + return nil // already torn down + } + return fmt.Errorf("while running shim delete: %w (%s)", err, strings.TrimSpace(string(out))) + } + return nil +} diff --git a/cmd/ateom-cloud-hypervisor/internal/kata/kata_integration_test.go b/cmd/ateom-cloud-hypervisor/internal/kata/kata_integration_test.go new file mode 100644 index 000000000..8d93d07c0 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/kata/kata_integration_test.go @@ -0,0 +1,283 @@ +//go:build linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kata + +import ( + "context" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + task "github.com/containerd/containerd/api/runtime/task/v2" + tasktypes "github.com/containerd/containerd/api/types/task" + "golang.org/x/sys/unix" +) + +// TestKataLifecycle drives a real containerd-shim-kata-v2 (no containerd) through +// the full RUN lifecycle in a cloud-hypervisor micro-VM, then pause/resume, and +// tears it down. It requires a host with kata + /dev/kvm and is gated behind +// KATA_INTEGRATION=1. +// +// Env: +// +// KATA_INTEGRATION=1 enable the test +// KATA_SHIM= shim binary (default /opt/kata/bin/containerd-shim-kata-v2) +// KATA_ROOTFS_SRC= a populated rootfs to copy into the bundle (e.g. an +// extracted busybox image). Required. +// KATA_STDIO=1 wire container stdout/stderr to files in the bundle. +// +// Run as root on a KVM-capable Linux host with kata installed, e.g.: +// +// sudo KATA_INTEGRATION=1 KATA_ROOTFS_SRC=/tmp/bb/rootfs ./kata.test -test.v -test.run Lifecycle +func TestKataLifecycle(t *testing.T) { + if os.Getenv("KATA_INTEGRATION") != "1" { + t.Skip("set KATA_INTEGRATION=1 to run (requires kata + /dev/kvm)") + } + rootfsSrc := os.Getenv("KATA_ROOTFS_SRC") + if rootfsSrc == "" { + t.Fatal("KATA_ROOTFS_SRC is required (a populated rootfs dir to copy into the bundle)") + } + shim := os.Getenv("KATA_SHIM") + if shim == "" { + shim = "/opt/kata/bin/containerd-shim-kata-v2" + } + + id := fmt.Sprintf("ateomchv-it-%d", os.Getpid()) + work := filepath.Join("/tmp", id) + bundle := filepath.Join(work, "bundle") + rootfs := filepath.Join(bundle, "rootfs") + if err := os.MkdirAll(rootfs, 0o755); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = os.RemoveAll(work) }) + + // Populate the bundle rootfs (atelet's model: a pre-populated rootfs dir). + if out, err := exec.Command("cp", "-a", rootfsSrc+"/.", rootfs+"/").CombinedOutput(); err != nil { + t.Fatalf("copying rootfs: %v: %s", err, out) + } + writeBundleConfig(t, bundle, id) + + // containerd creates a "log" fifo in the bundle and reads it; the kata shim + // opens it O_WRONLY for its logrus output (and blocks if nobody reads). + // Provide it and drain to t.Log so we can see why the shim behaves as it does. + logFifo := filepath.Join(bundle, "log") + if err := unix.Mkfifo(logFifo, 0o700); err != nil { + t.Fatalf("mkfifo log: %v", err) + } + go func() { + f, err := os.OpenFile(logFifo, os.O_RDONLY, 0) + if err != nil { + return + } + defer f.Close() + buf := make([]byte, 4096) + for { + n, err := f.Read(buf) + if n > 0 { + t.Logf("[shim-log] %s", buf[:n]) + } + if err != nil { + return + } + } + }() + + var createOpts CreateOptions + if os.Getenv("KATA_STDIO") == "1" { + createOpts.Stdout = filepath.Join(work, "stdout") + createOpts.Stderr = filepath.Join(work, "stderr") + } + + s := &Shim{ + Binary: shim, + ID: id, + Bundle: bundle, + Namespace: "default", + GRPCAddress: filepath.Join(work, "fake-containerd.sock"), + TTRPCAddress: filepath.Join(work, "fake-containerd.sock.ttrpc"), + Diagnostics: testWriter{t}, + Debug: true, + ConfigFile: os.Getenv("KATA_CONF_FILE"), // optional: test a generated config + } + + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + + // Always attempt teardown so a failure mid-test doesn't leak a VMM. + defer func() { + _ = s.Kill(ctx, 9, true) + shutCtx, c := context.WithTimeout(context.Background(), 15*time.Second) + _ = s.Shutdown(shutCtx, true) + c() + _ = s.Close() + clCtx, c2 := context.WithTimeout(context.Background(), 15*time.Second) + _ = s.CleanupAction(clCtx) + c2() + }() + + if err := s.Bootstrap(ctx); err != nil { + t.Fatalf("Bootstrap: %v", err) + } + t.Logf("shim ttrpc address: %s", s.Address()) + + pid, err := s.Create(ctx, createOpts) + if err != nil { + t.Fatalf("Create: %v", err) + } + t.Logf("Create ok, pid=%d", pid) + + // The CH api-socket should now exist for this sandbox id. + clh := CLHSocketPath(id) + if !waitFor(5*time.Second, func() bool { _, err := os.Stat(clh); return err == nil }) { + t.Errorf("clh api-socket %q did not appear", clh) + } else { + t.Logf("clh api-socket present: %s", clh) + } + + if _, err := s.Start(ctx); err != nil { + t.Fatalf("Start: %v", err) + } + t.Log("Start ok") + + assertStatus(t, ctx, s, tasktypes.Status_RUNNING) + + if err := s.Pause(ctx); err != nil { + t.Fatalf("Pause: %v", err) + } + assertStatus(t, ctx, s, tasktypes.Status_PAUSED) + t.Log("Pause ok") + + if err := s.Resume(ctx); err != nil { + t.Fatalf("Resume: %v", err) + } + assertStatus(t, ctx, s, tasktypes.Status_RUNNING) + t.Log("Resume ok") + + // Graceful-ish: kill the container, then delete, then shutdown (defer also + // force-cleans). + if err := s.Kill(ctx, 9, true); err != nil { + t.Errorf("Kill: %v", err) + } + time.Sleep(time.Second) + if err := s.Delete(ctx); err != nil { + t.Logf("Delete (non-fatal): %v", err) + } + t.Log("lifecycle complete") +} + +func assertStatus(t *testing.T, ctx context.Context, s *Shim, want tasktypes.Status) { + t.Helper() + resp, err := s.task.State(ctx, &task.StateRequest{ID: s.ID}) + if err != nil { + t.Fatalf("State: %v", err) + } + if resp.GetStatus() != want { + t.Errorf("status = %v, want %v", resp.GetStatus(), want) + } +} + +func waitFor(d time.Duration, cond func() bool) bool { + end := time.Now().Add(d) + for time.Now().Before(end) { + if cond() { + return true + } + time.Sleep(50 * time.Millisecond) + } + return cond() +} + +type testWriter struct{ t *testing.T } + +func (w testWriter) Write(p []byte) (int, error) { + w.t.Logf("%s", p) + return len(p), nil +} + +// writeBundleConfig emits an OCI spec mirroring what `ctr run --runtime +// io.containerd.kata.v2` produces (the proven-good shape). kata's OCI conversion +// nil-derefs without linux.resources, so a too-minimal spec crashes the shim. +func writeBundleConfig(t *testing.T, bundle, id string) { + t.Helper() + caps := []string{ + "CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_FSETID", "CAP_FOWNER", "CAP_MKNOD", + "CAP_NET_RAW", "CAP_SETGID", "CAP_SETUID", "CAP_SETFCAP", "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", "CAP_SYS_CHROOT", "CAP_KILL", "CAP_AUDIT_WRITE", + } + spec := map[string]any{ + "ociVersion": "1.2.0", + "process": map[string]any{ + "user": map[string]any{"uid": 0, "gid": 0, "additionalGids": []int{0, 10}}, + "args": []string{"sleep", "3600"}, + "env": []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, + "cwd": "/", + "capabilities": map[string]any{ + "bounding": caps, "effective": caps, "permitted": caps, + }, + "rlimits": []map[string]any{{"type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024}}, + "noNewPrivileges": true, + }, + "root": map[string]any{"path": "rootfs"}, + "mounts": []map[string]any{ + {"destination": "/proc", "type": "proc", "source": "proc", "options": []string{"nosuid", "noexec", "nodev"}}, + {"destination": "/dev", "type": "tmpfs", "source": "tmpfs", "options": []string{"nosuid", "strictatime", "mode=755", "size=65536k"}}, + {"destination": "/dev/pts", "type": "devpts", "source": "devpts", "options": []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}}, + {"destination": "/dev/shm", "type": "tmpfs", "source": "shm", "options": []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}}, + {"destination": "/dev/mqueue", "type": "mqueue", "source": "mqueue", "options": []string{"nosuid", "noexec", "nodev"}}, + {"destination": "/sys", "type": "sysfs", "source": "sysfs", "options": []string{"nosuid", "noexec", "nodev", "ro"}}, + {"destination": "/run", "type": "tmpfs", "source": "tmpfs", "options": []string{"nosuid", "strictatime", "mode=755", "size=65536k"}}, + }, + "linux": map[string]any{ + "resources": map[string]any{ + "devices": []map[string]any{ + {"allow": false, "access": "rwm"}, + {"allow": true, "type": "c", "major": 1, "minor": 3, "access": "rwm"}, + {"allow": true, "type": "c", "major": 1, "minor": 8, "access": "rwm"}, + {"allow": true, "type": "c", "major": 1, "minor": 7, "access": "rwm"}, + {"allow": true, "type": "c", "major": 5, "minor": 0, "access": "rwm"}, + {"allow": true, "type": "c", "major": 1, "minor": 5, "access": "rwm"}, + {"allow": true, "type": "c", "major": 1, "minor": 9, "access": "rwm"}, + {"allow": true, "type": "c", "major": 5, "minor": 1, "access": "rwm"}, + {"allow": true, "type": "c", "major": 136, "access": "rwm"}, + {"allow": true, "type": "c", "major": 5, "minor": 2, "access": "rwm"}, + }, + "cpu": map[string]any{"shares": 1024}, + }, + "cgroupsPath": "/ateomchv/" + id, + "namespaces": []map[string]any{ + {"type": "pid"}, + {"type": "ipc"}, + {"type": "uts"}, + {"type": "mount"}, + {"type": "network"}, + }, + "maskedPaths": []string{"/proc/acpi", "/proc/asound", "/proc/kcore", "/proc/keys", "/proc/latency_stats", "/proc/timer_list", "/proc/timer_stats", "/proc/sched_debug", "/sys/firmware", "/sys/devices/virtual/powercap", "/proc/scsi"}, + "readonlyPaths": []string{"/proc/bus", "/proc/fs", "/proc/irq", "/proc/sys", "/proc/sysrq-trigger"}, + }, + } + b, err := json.MarshalIndent(spec, "", " ") + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(bundle, "config.json"), b, 0o644); err != nil { + t.Fatal(err) + } +} diff --git a/cmd/ateom-cloud-hypervisor/internal/kata/restore.go b/cmd/ateom-cloud-hypervisor/internal/kata/restore.go new file mode 100644 index 000000000..e69a2bffa --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/kata/restore.go @@ -0,0 +1,211 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kata + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "time" +) + +// Per-sandbox runtime paths kata uses. The CH snapshot's config.json references +// these absolute paths (virtiofsd vhost-user socket, vsock socket), so restore +// must recreate them (or rewrite config.json) for the sandbox id. + +// VMDir is the per-sandbox runtime dir kata creates (holds clh-api.sock, +// clh.sock, virtiofsd.sock). +func VMDir(id string) string { return filepath.Join(vcVMDir, id) } + +// VirtiofsdSocketPath is the vhost-user-fs socket the CH snapshot's _fs1 device +// references; restore must start virtiofsd listening here. +func VirtiofsdSocketPath(id string) string { return filepath.Join(VMDir(id), "virtiofsd.sock") } + +// VsockSocketPath is the hybrid-vsock socket the CH snapshot's vsock device +// references; CH recreates the listener here on restore. +func VsockSocketPath(id string) string { return filepath.Join(VMDir(id), "clh.sock") } + +// SharedDir is the host directory kata virtio-fs-shares into the guest (the +// container rootfs lives here, as an overlay mount inside the shim's mount +// namespace). Its CONTENTS must be captured (CaptureSharedDir) at checkpoint and +// reconstructed (ReconstructSharedDir) at restore for find-paths to re-open them. +func SharedDir(id string) string { + return filepath.Join("/run/kata-containers/shared/sandboxes", id, "shared") +} + +// ShimPID returns the pid of the running shim daemon for this sandbox. The shim +// writes it to /shim.pid; falls back to scanning /proc for a shim +// process whose cmdline names this sandbox id (no external pgrep dependency). +func (s *Shim) ShimPID() (int, error) { + // In the foreground-server model the shim is our own child process, so its + // pid is authoritative (the shim does not write shim.pid and a /proc scan can + // miss it). Prefer it. + if s.cmd != nil && s.cmd.Process != nil && s.cmd.Process.Pid > 0 { + return s.cmd.Process.Pid, nil + } + if b, err := os.ReadFile(filepath.Join(s.Bundle, "shim.pid")); err == nil { + if pid, perr := strconv.Atoi(strings.TrimSpace(string(b))); perr == nil && pid > 0 { + return pid, nil + } + } + entries, err := os.ReadDir("/proc") + if err != nil { + return 0, fmt.Errorf("scanning /proc for shim pid: %w", err) + } + for _, e := range entries { + pid, err := strconv.Atoi(e.Name()) + if err != nil { + continue // not a pid dir + } + cmdline, err := os.ReadFile(filepath.Join("/proc", e.Name(), "cmdline")) + if err != nil { + continue + } + // cmdline args are NUL-separated. + args := string(cmdline) + if strings.Contains(args, "containerd-shim-kata-v2") && strings.Contains(args, s.ID) { + return pid, nil + } + } + return 0, fmt.Errorf("no shim process found for %q", s.ID) +} + +// CaptureSharedDir tars the sandbox's virtio-fs shared dir (the merged container +// rootfs) from INSIDE the shim's mount namespace into destTar. The content is +// only visible in the shim's mountns, so we nsenter into it; tar streams to +// stdout (a host file) to avoid path-in-namespace ambiguity. +// +// The guest must be paused first (so the rootfs is quiescent). +func (s *Shim) CaptureSharedDir(ctx context.Context, destTar string) error { + pid, err := s.ShimPID() + if err != nil { + return err + } + f, err := os.Create(destTar) + if err != nil { + return fmt.Errorf("creating %q: %w", destTar, err) + } + defer f.Close() + + cmd := exec.CommandContext(ctx, "nsenter", "-m", "-t", strconv.Itoa(pid), + "tar", "-C", SharedDir(s.ID), "-cf", "-", ".") + cmd.Stdout = f + var stderr strings.Builder + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("capturing shared dir via nsenter tar: %w (%s)", err, strings.TrimSpace(stderr.String())) + } + return nil +} + +// CaptureSharedDirLocal tars the shared dir for id from the CURRENT mount +// namespace into destTar. Use for ateom-owned (restored) actors, whose shared +// dir was built by ReconstructSharedDir in ateom's own namespace (no shim to +// nsenter). The guest must be paused first. +func CaptureSharedDirLocal(ctx context.Context, id, destTar string) error { + f, err := os.Create(destTar) + if err != nil { + return fmt.Errorf("creating %q: %w", destTar, err) + } + defer f.Close() + cmd := exec.CommandContext(ctx, "tar", "-C", SharedDir(id), "-cf", "-", ".") + cmd.Stdout = f + var stderr strings.Builder + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("capturing local shared dir: %w (%s)", err, strings.TrimSpace(stderr.String())) + } + return nil +} + +// ReconstructSharedDir extracts a CaptureSharedDir tar into the shared dir for +// id (a plain host directory; find-paths re-opens by relative path so this need +// not be a real overlay mount). +func ReconstructSharedDir(ctx context.Context, srcTar, id string) error { + dst := SharedDir(id) + if err := os.RemoveAll(dst); err != nil { + return fmt.Errorf("clearing shared dir %q: %w", dst, err) + } + if err := os.MkdirAll(dst, 0o700); err != nil { + return fmt.Errorf("creating shared dir %q: %w", dst, err) + } + cmd := exec.CommandContext(ctx, "tar", "-C", dst, "-xf", srcTar) + var stderr strings.Builder + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("reconstructing shared dir: %w (%s)", err, strings.TrimSpace(stderr.String())) + } + return nil +} + +// VirtiofsdOptions configures StartVirtiofsd. +type VirtiofsdOptions struct { + // Binary is the virtiofsd executable; defaults to "virtiofsd". + Binary string + // SocketPath is the vhost-user socket CH will connect to (VirtiofsdSocketPath). + SocketPath string + // SharedDir is the directory to serve (SharedDir(id)). + SharedDir string + // Log receives virtiofsd's stdout/stderr. + Log io.Writer +} + +// StartVirtiofsd launches virtiofsd in find-paths migration mode, +// matching the args kata uses, and waits until its socket appears. The returned +// cmd is owned by the caller (Kill on teardown). find-paths is required so the +// CH snapshot's vhost-user device-state reload (re-open by path) succeeds. +func StartVirtiofsd(ctx context.Context, o VirtiofsdOptions) (*exec.Cmd, error) { + bin := o.Binary + if bin == "" { + bin = "virtiofsd" + } + _ = os.Remove(o.SocketPath) + // Deliberately NOT exec.CommandContext: virtiofsd must outlive the Restore + // RPC whose ctx launched it (gRPC cancels the ctx when the handler returns, + // which would SIGKILL the vhost-user backend under the restored VM). The + // caller owns the returned cmd; the wait loop below honors ctx. + cmd := exec.Command(bin, + "--socket-path="+o.SocketPath, + "--shared-dir="+o.SharedDir, + "--cache=auto", + "--thread-pool-size=1", + "--announce-submounts", + "--migration-mode", "find-paths", + ) + cmd.Stdout = o.Log + cmd.Stderr = o.Log + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("starting virtiofsd: %w", err) + } + deadline := time.Now().Add(10 * time.Second) + for time.Now().Before(deadline) { + if _, err := os.Stat(o.SocketPath); err == nil { + return cmd, nil + } + select { + case <-ctx.Done(): + _ = cmd.Process.Kill() + return nil, ctx.Err() + case <-time.After(50 * time.Millisecond): + } + } + _ = cmd.Process.Kill() + return nil, fmt.Errorf("virtiofsd socket %q did not appear", o.SocketPath) +} diff --git a/cmd/ateom-cloud-hypervisor/internal/kata/restore_integration_test.go b/cmd/ateom-cloud-hypervisor/internal/kata/restore_integration_test.go new file mode 100644 index 000000000..bf66042ec --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/internal/kata/restore_integration_test.go @@ -0,0 +1,186 @@ +//go:build linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kata + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/agent-substrate/substrate/cmd/ateom-cloud-hypervisor/internal/ch" + "golang.org/x/sys/unix" +) + +// TestKataRestore exercises the full restore cycle against real kata + CH: +// boot a busybox CH micro-VM via the shim, capture the virtio-fs shared dir +// from the shim's mount namespace, pause+snapshot, tear the original down, then +// RESTORE by reconstructing the shared dir + relaunching virtiofsd + CH +// --restore + resume. A post-restore pause/resume roundtrip proves the restored +// guest is live. Gated behind KATA_INTEGRATION=1. +// +// Env (in addition to KATA_INTEGRATION / KATA_ROOTFS_SRC / KATA_SHIM): +// +// KATA_CH= cloud-hypervisor (default /usr/local/bin/cloud-hypervisor) +// KATA_VIRTIOFSD= virtiofsd with migration support (default /usr/local/bin/virtiofsd-patched) +func TestKataRestore(t *testing.T) { + if os.Getenv("KATA_INTEGRATION") != "1" { + t.Skip("set KATA_INTEGRATION=1 to run (requires kata + /dev/kvm)") + } + rootfsSrc := os.Getenv("KATA_ROOTFS_SRC") + if rootfsSrc == "" { + t.Fatal("KATA_ROOTFS_SRC is required") + } + shim := envOr("KATA_SHIM", "/opt/kata/bin/containerd-shim-kata-v2") + chBin := envOr("KATA_CH", "/usr/local/bin/cloud-hypervisor") + vfsdBin := envOr("KATA_VIRTIOFSD", "/usr/local/bin/virtiofsd-patched") + + id := fmt.Sprintf("ateomchv-rst-%d", os.Getpid()) + work := filepath.Join("/tmp", id) + bundle := filepath.Join(work, "bundle") + rootfs := filepath.Join(bundle, "rootfs") + if err := os.MkdirAll(rootfs, 0o755); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = os.RemoveAll(work) }) + if out, err := exec.Command("cp", "-a", rootfsSrc+"/.", rootfs+"/").CombinedOutput(); err != nil { + t.Fatalf("copying rootfs: %v: %s", err, out) + } + writeBundleConfig(t, bundle, id) + drainLogFifo(t, bundle) + + s := &Shim{ + Binary: shim, + ID: id, + Bundle: bundle, + Namespace: "default", + GRPCAddress: filepath.Join(work, "fake-containerd.sock"), + TTRPCAddress: filepath.Join(work, "fake-containerd.sock.ttrpc"), + Diagnostics: testWriter{t}, + Debug: true, + } + + ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second) + defer cancel() + + var vfsdCmd, chCmd *exec.Cmd + t.Cleanup(func() { + for _, c := range []*exec.Cmd{chCmd, vfsdCmd} { + if c != nil && c.Process != nil { + _ = c.Process.Kill() + } + } + _ = os.RemoveAll(VMDir(id)) + _ = os.RemoveAll(SharedDir(id)) + }) + + // --- Boot the original incarnation. --- + if err := s.Bootstrap(ctx); err != nil { + t.Fatalf("Bootstrap: %v", err) + } + if _, err := s.Create(ctx, CreateOptions{}); err != nil { + t.Fatalf("Create: %v", err) + } + if _, err := s.Start(ctx); err != nil { + t.Fatalf("Start: %v", err) + } + t.Log("original micro-VM running") + + // --- Capture shared dir (from the shim mountns) + snapshot. --- + tarPath := filepath.Join(work, "shared-dir.tar") + if err := s.CaptureSharedDir(ctx, tarPath); err != nil { + t.Fatalf("CaptureSharedDir: %v", err) + } + t.Logf("captured shared dir -> %s", tarPath) + + client := ch.NewClient(CLHSocketPath(id)) + if err := client.WaitReady(ctx, 10*time.Second); err != nil { + t.Fatalf("CH WaitReady: %v", err) + } + if err := client.Pause(ctx); err != nil { + t.Fatalf("Pause: %v", err) + } + snapDir := filepath.Join(work, "snapshot") + if err := client.Snapshot(ctx, snapDir); err != nil { + t.Fatalf("Snapshot: %v", err) + } + t.Logf("snapshot -> %s", snapDir) + + // --- Tear the original down fast (kill CH + shim; skip agent-pinging stop). --- + _ = client.Shutdown(ctx) + if pid, err := s.ShimPID(); err == nil { + _ = unix.Kill(pid, unix.SIGKILL) + } + _ = s.Close() + time.Sleep(time.Second) + + // --- RESTORE (same id; bypasses the shim). --- + if err := ReconstructSharedDir(ctx, tarPath, id); err != nil { + t.Fatalf("ReconstructSharedDir: %v", err) + } + if err := os.MkdirAll(VMDir(id), 0o700); err != nil { + t.Fatal(err) + } + vfsdCmd, err := StartVirtiofsd(ctx, VirtiofsdOptions{ + Binary: vfsdBin, + SocketPath: VirtiofsdSocketPath(id), + SharedDir: SharedDir(id), + Log: testWriter{t}, + }) + if err != nil { + t.Fatalf("StartVirtiofsd: %v", err) + } + + var rclient *ch.Client + chCmd, rclient, err = ch.Restore(ctx, ch.RestoreOptions{ + Binary: chBin, + APISocket: filepath.Join(VMDir(id), "clh-api-restore.sock"), + SourceDir: snapDir, + Stdout: testWriter{t}, + Stderr: testWriter{t}, + }) + if err != nil { + t.Fatalf("ch.Restore: %v", err) + } + t.Log("CH restored from snapshot") + + if err := rclient.Resume(ctx); err != nil { + t.Fatalf("Resume after restore: %v", err) + } + + // Liveness: a pause/resume roundtrip on the restored VM proves it is live. + if err := rclient.Pause(ctx); err != nil { + t.Errorf("post-restore Pause (liveness): %v", err) + } + if err := rclient.Resume(ctx); err != nil { + t.Errorf("post-restore Resume (liveness): %v", err) + } + t.Log("RESTORE OK: guest resumed and is live on a fresh CH process") + + // Teardown the restored incarnation. + _ = rclient.Shutdown(ctx) +} + +func envOr(key, def string) string { + if v := os.Getenv(key); v != "" { + return v + } + return def +} diff --git a/cmd/ateom-cloud-hypervisor/main.go b/cmd/ateom-cloud-hypervisor/main.go new file mode 100644 index 000000000..1785d4315 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/main.go @@ -0,0 +1,254 @@ +//go:build linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Command ateom-cloud-hypervisor is the kata + cloud-hypervisor micro-VM +// implementation of the ateompb.Ateom service, a peer to cmd/ateom-gvisor. +// +// It runs a substrate actor as a cloud-hypervisor micro-VM (launched via the +// kata guest model) and supports full suspend/resume by driving CH's native +// snapshot/restore underneath (see internal/ch). +package main + +import ( + "context" + "flag" + "fmt" + "log/slog" + "net" + "os" + "strings" + "sync" + + "github.com/agent-substrate/substrate/internal/ateinterceptors" + "github.com/agent-substrate/substrate/internal/ateompath" + "github.com/agent-substrate/substrate/internal/proto/ateompb" + "github.com/agent-substrate/substrate/internal/serverboot" + "github.com/agent-substrate/substrate/internal/version" + "github.com/hashicorp/go-reap" + "github.com/vishvananda/netns" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "golang.org/x/sys/unix" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var ( + podUID = flag.String("pod-uid", "", "The UID of the current pod") + + shimBinary = flag.String("shim-binary", "containerd-shim-kata-v2", "Path to the kata containerd shim v2 binary.") + + chBinary = flag.String("cloud-hypervisor-binary", "cloud-hypervisor", "Path to the cloud-hypervisor binary (used to relaunch on restore).") + + virtiofsdBinary = flag.String("virtiofsd-binary", "virtiofsd", "Path to the virtiofsd binary (needs find-paths migration support, >= 1.13; used on restore).") + + kataConfig = flag.String("kata-config", "", "Path to a kata configuration.toml (passed to the shim as KATA_CONF_FILE). Empty uses kata's default. atelet generates one pointing at runtime-fetched assets.") + + kataNamespace = flag.String("kata-namespace", "default", "containerd namespace the kata shim runs under.") + + kataDebug = flag.Bool("kata-debug", false, "Verbose kata debugging: shim -debug, hypervisor/agent/runtime enable_debug, and guest console (incl. kata-agent logs) forwarded into pod logs.") + + showVersion = flag.Bool("version", false, "Print version and exit.") + + // reapLock guards subprocess exec against the child reaper, mirroring + // ateom-gvisor. ateom-cloud-hypervisor will spawn the kata shim and CH + // processes under it in later milestones. + reapLock sync.RWMutex +) + +func main() { + flag.Parse() + if *showVersion { + fmt.Println(version.String()) + return + } + ctx := context.Background() + + if err := do(ctx); err != nil { + slog.ErrorContext(ctx, "Error while executing", slog.Any("err", err)) + os.Exit(1) + } +} + +func do(ctx context.Context) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + serverboot.InitLogger() + slog.InfoContext(ctx, "ateom-cloud-hypervisor booting", slog.String("version", version.String())) + + tp, err := serverboot.InitTracing(ctx, serverboot.TracingOptions{ + ServiceName: "ateom-cloud-hypervisor", + Sampler: sdktrace.ParentBased(sdktrace.NeverSample()), + }) + if err != nil { + serverboot.Fatal(ctx, "Failed to initialize tracing", err) + } + defer serverboot.ShutdownProvider("TracerProvider", tp.Shutdown) + + // Create ateom dir. + ateomDir := ateompath.AteomPath(*podUID) + if err := os.MkdirAll(ateomDir, 0o700); err != nil { + return fmt.Errorf("in os.MkdirAll(%q): %w", ateomDir, err) + } + + // Reap children reparented to us (kata shim / cloud-hypervisor in later + // milestones), guarded so our own exec.Cmd calls can take the wait. + go reap.ReapChildren(nil, nil, nil, &reapLock) + slog.InfoContext(ctx, "Child process reaper launched") + + // kata launches virtiofsd with --syslog, which delivers to /dev/log. In a + // pod there is no syslog daemon, so provide a sink or virtiofsd fails + // ("Unix syslog delivery error") and Create fails. + startSyslogSink(ctx) + + // kata's virtio-fs sharing depends on mount propagation: it slave-binds + // .../shared (served by virtiofsd) from .../mounts and expects the later + // per-container rootfs bind under mounts/ to propagate across. That only + // works if the underlying mount is SHARED. On a host systemd makes / + // rshared, but a container rootfs is rprivate (runc default), so the + // propagation silently never happens: the guest sees an empty rootfs and + // createContainer fails ENOENT. Self-bind /run/kata-containers and mark it + // rshared so kata's propagation chain works inside the pod. + if err := ensureSharedPropagation(ctx, "/run/kata-containers"); err != nil { + return fmt.Errorf("while making /run/kata-containers a shared mount: %w", err) + } + + // Clean up any old socket. + sockPath := ateompath.AteomSocketPath(*podUID) + if err := os.RemoveAll(sockPath); err != nil { + return fmt.Errorf("while removing %q: %w", sockPath, err) + } + + lis, err := net.Listen("unix", sockPath) + if err != nil { + return fmt.Errorf("while opening unix socket: %w", err) + } + + // Networking (mirrors ateom-gvisor's veth model): create a named interior + // netns; each activation builds a fresh veth pair into it (see net.go) and + // points kata at it. + interiorNetNS, err := createNetNSWithoutSwitching(ateompath.AteomNetNSName(*podUID)) + if err != nil { + return fmt.Errorf("while creating interior netns: %w", err) + } + + svr := grpc.NewServer( + grpc.StatsHandler(otelgrpc.NewServerHandler()), + grpc.UnaryInterceptor(ateinterceptors.ServerUnaryInterceptor), + ) + ateompb.RegisterAteomServer(svr, NewService(*podUID, *shimBinary, *chBinary, *virtiofsdBinary, *kataConfig, *kataNamespace, *kataDebug, interiorNetNS)) + reflection.Register(svr) + + slog.InfoContext(ctx, "ateom-cloud-hypervisor serving", slog.String("socket", sockPath)) + if err := svr.Serve(lis); err != nil { + return fmt.Errorf("while serving: %w", err) + } + return nil +} + +// ensureSharedPropagation makes path a mount point with rshared propagation +// (self-bind + MS_SHARED|MS_REC), so mounts created beneath it propagate to +// slave binds (kata's mounts/ -> shared/ chain). Idempotent: skips if path is +// already a shared mount point. +func ensureSharedPropagation(ctx context.Context, path string) error { + if err := os.MkdirAll(path, 0o750); err != nil { + return fmt.Errorf("creating %q: %w", path, err) + } + if b, err := os.ReadFile("/proc/self/mountinfo"); err == nil { + for _, line := range strings.Split(string(b), "\n") { + // mountinfo: ID parentID major:minor root mountpoint opts optional... - fstype ... + fields := strings.Fields(line) + if len(fields) >= 7 && fields[4] == path && strings.Contains(line, "shared:") { + slog.InfoContext(ctx, "Mount already shared", slog.String("path", path)) + return nil + } + } + } + if err := unix.Mount(path, path, "", unix.MS_BIND, ""); err != nil { + return fmt.Errorf("self-binding %q: %w", path, err) + } + if err := unix.Mount("", path, "", unix.MS_SHARED|unix.MS_REC, ""); err != nil { + return fmt.Errorf("marking %q rshared: %w", path, err) + } + slog.InfoContext(ctx, "Made mount rshared for kata virtio-fs propagation", slog.String("path", path)) + return nil +} + +// startSyslogSink binds a unixgram socket at /dev/log and drains it, so +// virtiofsd's --syslog delivery (which kata always enables) succeeds inside a +// pod that has no syslog daemon. Best-effort. +func startSyslogSink(ctx context.Context) { + const addr = "/dev/log" + _ = os.Remove(addr) + conn, err := net.ListenUnixgram("unixgram", &net.UnixAddr{Name: addr, Net: "unixgram"}) + if err != nil { + slog.WarnContext(ctx, "Failed to create /dev/log syslog sink; virtiofsd --syslog may fail", slog.Any("err", err)) + return + } + slog.InfoContext(ctx, "syslog sink listening", slog.String("addr", addr)) + go func() { + buf := make([]byte, 8192) + for { + if _, _, err := conn.ReadFromUnix(buf); err != nil { + return + } + } + }() +} + +// AteomService is the cloud-hypervisor implementation of ateompb.AteomServer. +type AteomService struct { + ateompb.UnimplementedAteomServer + + // lock serializes RPCs; like ateom-gvisor, the run/checkpoint/restore + // lifecycle is not safe to drive concurrently. + lock sync.Mutex + + podUID string + shimBinary string + chBinary string + virtiofsdBinary string + kataConfig string + namespace string + kataDebug bool + + // interiorNetNS hosts the per-activation actor veth peer (see net.go); + // kata is pointed at it. + interiorNetNS netns.NsHandle + + // running maps actor id -> the live micro-VM, kept so CheckpointWorkload can + // pause+snapshot+teardown the same sandbox (and RestoreWorkload can track the + // CH it relaunched). + running map[string]*runningActor +} + +var _ ateompb.AteomServer = (*AteomService)(nil) + +// NewService creates a new AteomService. +func NewService(podUID, shimBinary, chBinary, virtiofsdBinary, kataConfig, namespace string, kataDebug bool, interiorNetNS netns.NsHandle) *AteomService { + return &AteomService{ + podUID: podUID, + shimBinary: shimBinary, + chBinary: chBinary, + virtiofsdBinary: virtiofsdBinary, + kataConfig: kataConfig, + namespace: namespace, + kataDebug: kataDebug, + interiorNetNS: interiorNetNS, + running: map[string]*runningActor{}, + } +} diff --git a/cmd/ateom-cloud-hypervisor/main_unsupported.go b/cmd/ateom-cloud-hypervisor/main_unsupported.go new file mode 100644 index 000000000..2d8a864c0 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/main_unsupported.go @@ -0,0 +1,27 @@ +//go:build !linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "os" +) + +func main() { + fmt.Fprintln(os.Stderr, "ateom-cloud-hypervisor is only supported on Linux") + os.Exit(1) +} diff --git a/cmd/ateom-cloud-hypervisor/net.go b/cmd/ateom-cloud-hypervisor/net.go new file mode 100644 index 000000000..8b674c989 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/net.go @@ -0,0 +1,563 @@ +//go:build linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +// Actor networking mirrors cmd/ateom-gvisor's veth model: a fresh +// point-to-point veth pair per activation, with the worker side (ateom0, +// 169.254.17.1/30) staying in the pod netns next to the pod's real eth0, and +// the peer moved into the interior netns, renamed eth0, and given the stable +// actor address 169.254.17.2/30. nftables rules in the pod netns masquerade +// actor egress behind the pod IP and DNAT inbound pod-IP:80 to the actor. +// +// kata consumes the interior netns exactly like a CNI-provisioned container +// netns: its tcfilter network model builds a tap cross-connected to eth0 (the +// veth peer) and gives the guest eth0's address. Because that address is a +// CONSTANT, a restored guest's frozen network config stays valid on any pod — +// no in-guest reconfiguration needed. +// +// (Copied with light adaptation from cmd/ateom-gvisor; expected to be +// de-duplicated into a shared package later.) + +import ( + "context" + "errors" + "fmt" + "log/slog" + "net" + "os" + "runtime" + + "github.com/google/nftables" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" + "golang.org/x/sys/unix" + + "github.com/agent-substrate/substrate/internal/serverboot" +) + +const ( + hostVethName = "ateom0" + actorVethName = "eth0" + actorVethTempName = "ateom1" + hostVethCIDR = "169.254.17.1/30" + actorVethCIDR = "169.254.17.2/30" + actorVethGateway = "169.254.17.1" + actorVethIP = "169.254.17.2" + actorNftTableName = "ateom_actor" + + // hostVethMAC is deliberately FIXED (locally administered), unlike + // ateom-gvisor where the kernel's random veth MAC is fine. A CH snapshot + // freezes the guest kernel's ARP cache, including the entry for the + // gateway 169.254.17.1; restoring against a new veth pair with a random + // MAC would blackhole guest egress until that entry expires. A constant + // gateway MAC keeps the frozen entry valid on every pod. + hostVethMAC = "02:a8:1e:00:00:01" +) + +// setupActorNetwork builds a fresh point-to-point network between the worker +// pod netns and the kata interior netns (see the package comment). Idempotent +// via cleanup-before-setup; also sweeps stale kata taps out of the interior +// netns so the sandbox always builds on a clean slate. +func (s *AteomService) setupActorNetwork(ctx context.Context) (retErr error) { + s.cleanupActorNetworkOrExit(ctx, "Failed to clean up stale actor network before setup") + defer func() { + if retErr != nil { + s.cleanupActorNetworkOrExit(ctx, "Failed to clean up partially configured actor network") + } + }() + + podIP, err := podIPv4() + if err != nil { + return fmt.Errorf("while resolving pod IPv4 address: %w", err) + } + + // Sweep leftover links (kata taps from torn-down runs, restore taps) from + // the persistent interior netns before the new veth peer arrives. + if err := netNSDo(ctx, s.interiorNetNS, func(ctx context.Context) error { + links, err := netlink.LinkList() + if err != nil { + return fmt.Errorf("while listing interior netns links: %w", err) + } + for _, l := range links { + if l.Attrs().Name == "lo" { + continue + } + if err := netlink.LinkDel(l); err != nil { + slog.WarnContext(ctx, "Failed to delete leftover interior link", slog.String("link", l.Attrs().Name), slog.Any("err", err)) + } + } + return nil + }); err != nil { + return err + } + + hostAddr, err := parseAddr(hostVethCIDR) + if err != nil { + return err + } + hostMAC, err := net.ParseMAC(hostVethMAC) + if err != nil { + return fmt.Errorf("while parsing host veth MAC: %w", err) + } + + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{ + Name: hostVethName, + HardwareAddr: hostMAC, + }, + PeerName: actorVethTempName, + } + if err := netlink.LinkAdd(veth); err != nil { + return fmt.Errorf("while creating actor veth pair: %w", err) + } + + hostLink, err := netlink.LinkByName(hostVethName) + if err != nil { + return fmt.Errorf("while getting host veth: %w", err) + } + if err := netlink.AddrReplace(hostLink, hostAddr); err != nil { + return fmt.Errorf("while assigning host veth address: %w", err) + } + if err := netlink.LinkSetUp(hostLink); err != nil { + return fmt.Errorf("while bringing up host veth: %w", err) + } + + actorLink, err := netlink.LinkByName(actorVethTempName) + if err != nil { + return fmt.Errorf("while getting actor veth peer: %w", err) + } + if err := netlink.LinkSetNsFd(actorLink, int(s.interiorNetNS)); err != nil { + return fmt.Errorf("while moving actor veth peer into interior netns: %w", err) + } + + if err := netNSDo(ctx, s.interiorNetNS, configureActorVeth); err != nil { + return fmt.Errorf("while configuring actor veth in interior netns: %w", err) + } + + if err := enableIPv4Forwarding(); err != nil { + return err + } + if err := installActorNftablesRules(podIP); err != nil { + return err + } + + return nil +} + +func configureActorVeth(ctx context.Context) error { + // Run inside the interior netns after setupActorNetwork moves the veth peer + // there. kata reads link names, addresses, and routes from this namespace + // when the sandbox starts, so the peer is renamed to eth0 and configured + // like a normal container interface; the guest is configured identically by + // the kata agent. + loLink, err := netlink.LinkByName("lo") + if err != nil { + return fmt.Errorf("while acquiring lo in interior netns: %w", err) + } + if err := netlink.LinkSetUp(loLink); err != nil { + return fmt.Errorf("while bringing up lo in interior netns: %w", err) + } + + actorLink, err := netlink.LinkByName(actorVethTempName) + if err != nil { + return fmt.Errorf("while acquiring actor veth in interior netns: %w", err) + } + if err := netlink.LinkSetName(actorLink, actorVethName); err != nil { + return fmt.Errorf("while renaming actor veth to %q: %w", actorVethName, err) + } + actorLink, err = netlink.LinkByName(actorVethName) + if err != nil { + return fmt.Errorf("while reacquiring actor veth in interior netns: %w", err) + } + + actorAddr, err := parseAddr(actorVethCIDR) + if err != nil { + return err + } + if err := netlink.AddrReplace(actorLink, actorAddr); err != nil { + return fmt.Errorf("while assigning actor veth address: %w", err) + } + if err := netlink.LinkSetUp(actorLink); err != nil { + return fmt.Errorf("while bringing up actor veth: %w", err) + } + + gw := net.ParseIP(actorVethGateway).To4() + if gw == nil { + return fmt.Errorf("invalid actor veth gateway %q", actorVethGateway) + } + if err := netlink.RouteReplace(&netlink.Route{ + LinkIndex: actorLink.Attrs().Index, + Gw: gw, + }); err != nil { + return fmt.Errorf("while installing actor default route: %w", err) + } + + return nil +} + +// cleanupActorNetwork removes all per-activation network state owned by ateom. +// Intentionally idempotent: runs before setup, after checkpoint, and from +// setup-failure cleanup. +func (s *AteomService) cleanupActorNetwork(ctx context.Context) error { + if err := removeActorNftablesRules(); err != nil { + return err + } + + var cleanupErr error + if link, err := netlink.LinkByName(hostVethName); err == nil { + if err := netlink.LinkDel(link); err != nil { + cleanupErr = errors.Join(cleanupErr, fmt.Errorf("while deleting host veth: %w", err)) + slog.WarnContext(ctx, "Failed to delete host veth; continuing actor netns cleanup", slog.Any("err", err)) + } + } else if _, ok := err.(netlink.LinkNotFoundError); !ok { + cleanupErr = errors.Join(cleanupErr, fmt.Errorf("while looking up host veth: %w", err)) + slog.WarnContext(ctx, "Failed to look up host veth; continuing actor netns cleanup", slog.Any("err", err)) + } + + if err := netNSDo(ctx, s.interiorNetNS, func(_ context.Context) error { + for _, name := range []string{actorVethName, actorVethTempName} { + link, err := netlink.LinkByName(name) + if err == nil { + if err := netlink.LinkDel(link); err != nil { + return fmt.Errorf("while deleting interior veth %q: %w", name, err) + } + continue + } + if _, ok := err.(netlink.LinkNotFoundError); !ok { + return fmt.Errorf("while looking up interior veth %q: %w", name, err) + } + } + return nil + }); err != nil { + cleanupErr = errors.Join(cleanupErr, fmt.Errorf("while cleaning interior netns links: %w", err)) + } + + return cleanupErr +} + +func (s *AteomService) cleanupActorNetworkOrExit(ctx context.Context, msg string) { + if err := s.cleanupActorNetwork(ctx); err != nil { + serverboot.Fatal(ctx, msg, err) + } +} + +// podIPv4 resolves the worker pod IPv4 address from the pod namespace's real +// eth0 (which stays in the pod namespace in the veth model). +func podIPv4() (net.IP, error) { + eth0Link, err := netlink.LinkByName("eth0") + if err != nil { + return nil, fmt.Errorf("while getting pod eth0: %w", err) + } + addrs, err := netlink.AddrList(eth0Link, netlink.FAMILY_V4) + if err != nil { + return nil, fmt.Errorf("while listing pod eth0 addresses: %w", err) + } + for _, addr := range addrs { + if addr.IP == nil { + continue + } + if ip := addr.IP.To4(); ip != nil { + return ip, nil + } + } + return nil, fmt.Errorf("pod eth0 has no IPv4 address") +} + +func parseAddr(cidr string) (*netlink.Addr, error) { + addr, err := netlink.ParseAddr(cidr) + if err != nil { + return nil, fmt.Errorf("while parsing address %q: %w", cidr, err) + } + return addr, nil +} + +func enableIPv4Forwarding() error { + // Actor packets enter the worker pod via the host-side veth and leave + // through the pod's eth0; the kernel will not route between them otherwise. + if err := os.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte("1\n"), 0o644); err != nil { + return fmt.Errorf("while enabling IPv4 forwarding in worker pod netns: %w", err) + } + return nil +} + +func installActorNftablesRules(podIP net.IP) error { + // Dedicated ateom-owned IPv4 table (cheap cleanup, no CNI chain mutation): + // * postrouting: masquerade actor egress (169.254.17.2) behind the pod IP. + // * prerouting: DNAT pod-IP:80/tcp to the actor veth IP. + // * forward: accept forwarded packets between the actor veth and pod eth0. + // Mirrors cmd/ateom-gvisor (same compatibility-bridge caveats and TODOs). + if err := removeActorNftablesRules(); err != nil { + return err + } + + c := &nftables.Conn{} + table := &nftables.Table{ + Family: nftables.TableFamilyIPv4, + Name: actorNftTableName, + } + c.AddTable(table) + + prerouting := c.AddChain(&nftables.Chain{ + Name: "prerouting", + Table: table, + Type: nftables.ChainTypeNAT, + Hooknum: nftables.ChainHookPrerouting, + Priority: nftables.ChainPriorityNATDest, + }) + preroutingExprs := append(ipDestinationEqual(podIP.String()), tcpDestinationPortEqual(80)...) + preroutingExprs = append(preroutingExprs, + &expr.Immediate{ + Register: 1, + Data: net.ParseIP(actorVethIP).To4(), + }, + &expr.Immediate{ + Register: 2, + Data: binaryutil.BigEndian.PutUint16(80), + }, + &expr.NAT{ + Type: expr.NATTypeDestNAT, + Family: unix.NFPROTO_IPV4, + RegAddrMin: 1, + RegProtoMin: 2, + }, + ) + c.AddRule(&nftables.Rule{ + Table: table, + Chain: prerouting, + Exprs: preroutingExprs, + }) + + postrouting := c.AddChain(&nftables.Chain{ + Name: "postrouting", + Table: table, + Type: nftables.ChainTypeNAT, + Hooknum: nftables.ChainHookPostrouting, + Priority: nftables.ChainPriorityNATSource, + }) + c.AddRule(&nftables.Rule{ + Table: table, + Chain: postrouting, + Exprs: append(ipSourceEqual(actorVethIP), &expr.Masq{}), + }) + + acceptPolicy := nftables.ChainPolicyAccept + forward := c.AddChain(&nftables.Chain{ + Name: "forward", + Table: table, + Type: nftables.ChainTypeFilter, + Hooknum: nftables.ChainHookForward, + Priority: nftables.ChainPriorityFilter, + Policy: &acceptPolicy, + }) + c.AddRule(&nftables.Rule{ + Table: table, + Chain: forward, + Exprs: []expr.Any{ + &expr.Verdict{Kind: expr.VerdictAccept}, + }, + }) + + if err := c.Flush(); err != nil { + return fmt.Errorf("while installing actor nftables rules: %w", err) + } + return nil +} + +func removeActorNftablesRules() error { + c := &nftables.Conn{} + tables, err := c.ListTablesOfFamily(nftables.TableFamilyIPv4) + if err != nil { + return fmt.Errorf("while listing nftables tables: %w", err) + } + for _, table := range tables { + if table.Name != actorNftTableName { + continue + } + c.DelTable(table) + if err := c.Flush(); err != nil { + return fmt.Errorf("while deleting actor nftables table: %w", err) + } + return nil + } + return nil +} + +func ipSourceEqual(ip string) []expr.Any { + return ipPayloadEqual(12, ip) +} + +func ipDestinationEqual(ip string) []expr.Any { + return ipPayloadEqual(16, ip) +} + +func ipPayloadEqual(offset uint32, ip string) []expr.Any { + return []expr.Any{ + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseNetworkHeader, + Offset: offset, + Len: 4, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: net.ParseIP(ip).To4(), + }, + } +} + +func tcpDestinationPortEqual(port uint16) []expr.Any { + return []expr.Any{ + &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_TCP}, + }, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 2, + Len: 2, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: binaryutil.BigEndian.PutUint16(port), + }, + } +} + +// createNetNSWithoutSwitching creates a named netns and returns its handle, +// restoring the caller's current netns before returning. +func createNetNSWithoutSwitching(name string) (netns.NsHandle, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + curNetNS, err := netns.Get() + if err != nil { + return -1, fmt.Errorf("while getting current netns: %w", err) + } + defer func() { + if err := netns.Set(curNetNS); err != nil { + panic(fmt.Sprintf("Failed to restore original netns: %v", err)) + } + }() + + interiorNetNS, err := netns.NewNamed(name) + if err != nil { + return -1, fmt.Errorf("while creating interior network namespace: %w", err) + } + return interiorNetNS, nil +} + +// netNSDo runs do() with the OS thread switched into targetNS, then restores it. +func netNSDo(ctx context.Context, targetNS netns.NsHandle, do func(context.Context) error) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + curNetNS, err := netns.Get() + if err != nil { + return fmt.Errorf("while getting current netns: %w", err) + } + defer func() { + if err := netns.Set(curNetNS); err != nil { + panic(fmt.Sprintf("Failed to restore original netns: %v", err)) + } + }() + + if err := netns.Set(targetNS); err != nil { + return fmt.Errorf("setting target netns: %w", err) + } + if err := do(ctx); err != nil { + return fmt.Errorf("while executing function in target netns: %w", err) + } + return nil +} + +// setupRestoreTap recreates, in the interior netns, the tap + TC-mirror wiring +// kata's tcfilter network model builds at boot: a tap device cross-connected to +// eth0 (the actor veth peer) with mirred-redirect ingress filters in both +// directions. Returns the open tap FDs (one per queue pair) for +// cloud-hypervisor to adopt via vm.restore net_fds (the snapshot's virtio-net +// device is fd-backed, so CH requires fresh FDs on restore). Call after +// setupActorNetwork. +func (s *AteomService) setupRestoreTap(ctx context.Context, name string, queuePairs int) ([]*os.File, error) { + var fds []*os.File + err := netNSDo(ctx, s.interiorNetNS, func(ctx context.Context) error { + eth0, err := netlink.LinkByName(actorVethName) + if err != nil { + return fmt.Errorf("acquiring actor veth in interior netns: %w", err) + } + if old, lerr := netlink.LinkByName(name); lerr == nil { + _ = netlink.LinkDel(old) + } + flags := netlink.TUNTAP_NO_PI | netlink.TUNTAP_VNET_HDR + if queuePairs > 1 { + flags |= netlink.TUNTAP_MULTI_QUEUE + } + tap := &netlink.Tuntap{ + LinkAttrs: netlink.LinkAttrs{Name: name, MTU: eth0.Attrs().MTU}, + Mode: netlink.TUNTAP_MODE_TAP, + Flags: flags, + Queues: queuePairs, + } + if err := netlink.LinkAdd(tap); err != nil { + return fmt.Errorf("creating tap %q: %w", name, err) + } + fds = tap.Fds + if err := netlink.LinkSetUp(tap); err != nil { + return fmt.Errorf("bringing up tap %q: %w", name, err) + } + // Cross-connect: everything arriving on the veth peer redirects out the + // tap and vice versa (kata's TCFilterModel: ingress qdisc + match-all u32 + // with a mirred egress-redirect action, here via U32.RedirIndex). + for _, pair := range [][2]netlink.Link{{eth0, tap}, {tap, eth0}} { + qdisc := &netlink.Ingress{QdiscAttrs: netlink.QdiscAttrs{ + LinkIndex: pair[0].Attrs().Index, + Parent: netlink.HANDLE_INGRESS, + Handle: netlink.MakeHandle(0xffff, 0), + }} + if err := netlink.QdiscReplace(qdisc); err != nil { + return fmt.Errorf("adding ingress qdisc to %q: %w", pair[0].Attrs().Name, err) + } + filter := &netlink.U32{ + FilterAttrs: netlink.FilterAttrs{ + LinkIndex: pair[0].Attrs().Index, + Parent: netlink.MakeHandle(0xffff, 0), + Priority: 1, + Protocol: unix.ETH_P_ALL, + }, + ClassId: netlink.MakeHandle(1, 1), + RedirIndex: pair[1].Attrs().Index, + } + if err := netlink.FilterAdd(filter); err != nil { + return fmt.Errorf("adding mirred filter %s -> %s: %w", pair[0].Attrs().Name, pair[1].Attrs().Name, err) + } + } + return nil + }) + if err != nil { + for _, f := range fds { + _ = f.Close() + } + return nil, err + } + return fds, nil +} diff --git a/cmd/ateom-cloud-hypervisor/run.go b/cmd/ateom-cloud-hypervisor/run.go new file mode 100644 index 000000000..9c8de05d5 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/run.go @@ -0,0 +1,711 @@ +//go:build linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "encoding/json" + "fmt" + "log/slog" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/agent-substrate/substrate/cmd/ateom-cloud-hypervisor/internal/ch" + "github.com/agent-substrate/substrate/cmd/ateom-cloud-hypervisor/internal/kata" + "github.com/agent-substrate/substrate/internal/ateompath" + "github.com/agent-substrate/substrate/internal/proto/ateompb" + ctrtypes "github.com/containerd/containerd/api/types" + "github.com/opencontainers/runtime-spec/specs-go" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// runningActor holds the live state for one actor's micro-VM. +// +// Two ownership modes: +// - kata-shim-owned (from RunWorkload): shim != nil; kata owns the CH process. +// - ateom-owned (from RestoreWorkload): shim == nil; ateom relaunched CH + +// virtiofsd directly and owns those processes. +type runningActor struct { + containerName string + + // kata-shim-owned + shim *kata.Shim + + // ateom-owned (post-restore) + chCmd *exec.Cmd + vfsdCmd *exec.Cmd + // apiSocket is the CH api-socket for an ateom-owned (restored) VMM; empty + // for kata-shim-owned actors (use kata.CLHSocketPath then). + apiSocket string +} + +// sharedDirTar is the file name (under the checkpoint/restore dir) holding the +// captured virtio-fs shared-dir contents shipped alongside the CH snapshot. +const sharedDirTar = "shared-dir.tar" + +// Asset names in RunWorkloadRequest.runtime_asset_paths (set by atelet's +// fetchRuntimeAssets, keyed by the ActorTemplate runtime asset names). +const ( + assetShim = "kata-shim" + assetCH = "cloud-hypervisor" + assetVirtiofsd = "virtiofsd" + assetKernel = "kata-kernel" + assetImage = "kata-image" + assetConfig = "kata-config" +) + +// resolvedRuntime holds the concrete binary/config paths for a request, taken +// from fetched runtime assets when present, else the process flags. +type resolvedRuntime struct { + shim string + ch string + virtiofsd string + configFile string +} + +func firstNonEmpty(a, b string) string { + if a != "" { + return a + } + return b +} + +// resolveRuntime determines the kata shim / cloud-hypervisor / virtiofsd binaries +// and the kata config for a request. atelet fetches these content-addressed and +// passes their local paths; when a base config + kernel + image are present we +// render a configuration.toml pointing at the fetched paths (fetch-not-bake). +// Falls back to the process flags for anything not supplied. +func (s *AteomService) resolveRuntime(actorDir string, paths map[string]string) (resolvedRuntime, error) { + r := resolvedRuntime{ + shim: firstNonEmpty(paths[assetShim], s.shimBinary), + ch: firstNonEmpty(paths[assetCH], s.chBinary), + virtiofsd: firstNonEmpty(paths[assetVirtiofsd], s.virtiofsdBinary), + configFile: s.kataConfig, + } + base, kernel, image := paths[assetConfig], paths[assetKernel], paths[assetImage] + if base != "" && kernel != "" && image != "" { + baseBytes, err := os.ReadFile(base) + if err != nil { + return r, fmt.Errorf("reading base kata config %q: %w", base, err) + } + // TODO: make guest VM memory size a first-class ActorTemplate field and + // rewrite default_memory here (like the asset paths) instead of requiring + // per-size configuration.toml asset variants. Suspend/resume latency + // scales directly with guest memory (CH's snapshot scans/writes the whole + // memfd): on GKE pd-balanced disks, a 512MiB guest measured ~3.4s suspend + // / ~1.6s resume vs ~18s / ~4.4s at kata's stock default_memory=2048. + rendered, err := kata.RenderConfig(baseBytes, kata.ConfigAssets{ + Kernel: kernel, Image: image, Hypervisor: r.ch, Virtiofsd: r.virtiofsd, + }) + if err != nil { + return r, fmt.Errorf("rendering kata config: %w", err) + } + if s.kataDebug { + // Verbose kata: hypervisor/agent/runtime debug on, guest console (with + // the kata-agent's logs) forwarded into the shim log -> pod logs. + rendered = kata.EnableDebug(rendered) + } + cfgPath := filepath.Join(actorDir, "configuration.toml") + if err := os.MkdirAll(actorDir, 0o700); err != nil { + return r, fmt.Errorf("creating actor dir: %w", err) + } + if err := os.WriteFile(cfgPath, rendered, 0o600); err != nil { + return r, fmt.Errorf("writing rendered kata config: %w", err) + } + r.configFile = cfgPath + } + return r, nil +} + +// RunWorkload boots the actor as a kata + cloud-hypervisor micro-VM. +// +// Contract with atelet (mirrors ateom-gvisor): +// - The runtime assets (kata shim, guest kernel, rootfs, cloud-hypervisor, +// virtiofsd) are on disk and configured. +// - The OCI bundle (config.json + populated rootfs/) is prepared per container. +// +// ateom drives the kata shim v2 ttrpc Task API directly (no containerd daemon): +// Bootstrap (start action) -> Create (boots the CH VM) -> Start. The CH api +// socket then lives at kata.CLHSocketPath(id), which CheckpointWorkload drives. +// +// Proven end-to-end by TestKataLifecycle: boot + run + pause + resume of a +// busybox container in a CH micro-VM, no containerd. +func (s *AteomService) RunWorkload(ctx context.Context, req *ateompb.RunWorkloadRequest) (resp *ateompb.RunWorkloadResponse, retErr error) { + s.lock.Lock() + defer s.lock.Unlock() + + ns := req.GetActorTemplateNamespace() + name := req.GetActorTemplateName() + id := req.GetActorId() + + containers := req.GetSpec().GetContainers() + if len(containers) != 1 { + // POC: one container per sandbox. Multi-container pods are future work. + return nil, status.Errorf(codes.Unimplemented, "ateom-cloud-hypervisor supports exactly one container, got %d", len(containers)) + } + containerName := containers[0].GetName() + + // Networking (mirrors ateom-gvisor's veth model): build the per-activation + // veth into the interior netns and point kata at it; kata wires the guest + // to the stable actor address via its tap + TC mirror. + if err := s.setupActorNetwork(ctx); err != nil { + return nil, fmt.Errorf("while setting up actor network: %w", err) + } + defer func() { + if retErr != nil { + if cleanupErr := s.cleanupActorNetwork(ctx); cleanupErr != nil { + slog.WarnContext(ctx, "Failed to clean up actor network after Run failure", slog.Any("err", cleanupErr)) + } + } + }() + + bundle := ateompath.OCIBundlePath(ns, name, id, containerName) + if err := ensureKataCompatibleSpec(bundle, id, ateompath.AteomNetNSPath(s.podUID)); err != nil { + return nil, fmt.Errorf("while preparing kata OCI spec: %w", err) + } + + actorDir := ateompath.ActorPath(ns, name, id) + rr, err := s.resolveRuntime(actorDir, req.GetRuntimeAssetPaths()) + if err != nil { + return nil, fmt.Errorf("while resolving runtime assets: %w", err) + } + shim := &kata.Shim{ + Binary: rr.shim, + ID: id, + Bundle: bundle, + Namespace: s.namespace, + // No containerd: these only need to be stable, unique paths. The shim + // derives its ttrpc socket from (GRPCAddress, Namespace, ID) and would + // publish events to TTRPCAddress (best-effort, logged if unreachable). + GRPCAddress: filepath.Join(actorDir, "shim-containerd.sock"), + TTRPCAddress: filepath.Join(actorDir, "shim-containerd.sock.ttrpc"), + ConfigFile: rr.configFile, + Diagnostics: slogWriter{ctx}, + Debug: s.kataDebug, + } + + // Clear any leftover kata host-side state for this (deterministic) sandbox id + // from a prior failed/torn-down attempt, so the shim's share + virtiofsd + // socket setup doesn't collide ("address already in use" / "directory not + // empty"). Since we drive the shim directly, failed Creates don't self-clean. + kata.CleanupSandboxState(id) + + slog.InfoContext(ctx, "Bootstrapping kata shim", slog.String("id", id), slog.String("bundle", bundle)) + if err := shim.Bootstrap(ctx); err != nil { + return nil, fmt.Errorf("while bootstrapping kata shim: %w", err) + } + // On any failure after Bootstrap, tear the shim down so the pod isn't wedged. + defer func() { + if retErr != nil { + _ = shim.Shutdown(ctx, true) + _ = shim.Close() + _ = shim.CleanupAction(ctx) + } + }() + + // Pass the rootfs as a bind mount, mirroring what containerd's snapshotter + // provides: the mount SOURCE must differ from the target (/rootfs), + // so kata's shim bind-mounts source->target and virtio-fs-shares the target. + // atelet populates /rootfs directly; a self-bind (source==target) is + // not shared, leaving the guest rootfs empty -> agent createContainer ENOENT. + // Relocate the populated rootfs to a sibling source dir (once per bundle) and + // point kata at it, with an empty /rootfs as the mount target. + rootfsDir := filepath.Join(bundle, "rootfs") + rootfsSrc := filepath.Join(bundle, "rootfs-src") + if fi, statErr := os.Stat(rootfsSrc); statErr != nil || !fi.IsDir() { + if err := os.Rename(rootfsDir, rootfsSrc); err != nil { + return nil, fmt.Errorf("relocating populated rootfs to source dir: %w", err) + } + } + if err := os.MkdirAll(rootfsDir, 0o755); err != nil { + return nil, fmt.Errorf("creating rootfs mount target: %w", err) + } + if _, err := shim.Create(ctx, kata.CreateOptions{ + Rootfs: []*ctrtypes.Mount{{ + Type: "bind", + Source: rootfsSrc, + Options: []string{"bind", "rw"}, + }}, + }); err != nil { + return nil, fmt.Errorf("while creating container/VM: %w", err) + } + slog.InfoContext(ctx, "Micro-VM created", slog.String("clh_socket", kata.CLHSocketPath(id))) + + if _, err := shim.Start(ctx); err != nil { + return nil, fmt.Errorf("while starting container: %w", err) + } + + s.running[id] = &runningActor{shim: shim, containerName: containerName} + slog.InfoContext(ctx, "Actor started", slog.String("id", id)) + return &ateompb.RunWorkloadResponse{}, nil +} + +// ensureKataCompatibleSpec augments the bundle's config.json with the fields +// kata's OCI conversion requires but atelet's (gVisor-oriented) spec omits. +// Without linux.resources, kata's ContainerConfig nil-derefs and the shim +// crashes. This shaper is a bridge; a future atelet change should emit +// runtime-appropriate specs so it can retire. +func ensureKataCompatibleSpec(bundle, id, netnsPath string) error { + specPath := filepath.Join(bundle, "config.json") + b, err := os.ReadFile(specPath) + if err != nil { + return fmt.Errorf("reading %q: %w", specPath, err) + } + var spec specs.Spec + if err := json.Unmarshal(b, &spec); err != nil { + return fmt.Errorf("parsing %q: %w", specPath, err) + } + + if spec.Linux == nil { + spec.Linux = &specs.Linux{} + } + if spec.Linux.Resources == nil { + spec.Linux.Resources = defaultKataResources() + } + if spec.Linux.CgroupsPath == "" { + spec.Linux.CgroupsPath = "/ateomchv/" + id + } + + // atelet's spec carries gVisor pause-model CRI annotations + // (container-type=container, sandbox-id=pause). kata reads those and waits + // for a separate "pause" sandbox that we never create, failing with "the + // sandbox hasn't been created". Strip them so kata treats this single + // container as its own sandbox (creates the VM), as in the integration tests. + for k := range spec.Annotations { + if strings.HasPrefix(k, "io.kubernetes.cri.") { + delete(spec.Annotations, k) + } + } + + // Point the network namespace at our interior netns (which holds the pod's + // eth0); kata finds eth0 there and wires it to the VM's virtio-net. + netnsSet := false + for i := range spec.Linux.Namespaces { + if spec.Linux.Namespaces[i].Type == specs.NetworkNamespace { + spec.Linux.Namespaces[i].Path = netnsPath + netnsSet = true + } + } + if !netnsSet { + spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{ + Type: specs.NetworkNamespace, Path: netnsPath, + }) + } + + // Replace atelet's gVisor-oriented mounts (minimal /dev tmpfs, a + // /etc/resolv.conf host bind that ENOENTs against the distroless rootfs) with + // the exact set `ctr run --runtime io.containerd.kata.v2` emits, which kata's + // agent accepts. (POC shaper; pod DNS integration is future work.) + spec.Mounts = defaultKataMounts() + + out, err := json.MarshalIndent(&spec, "", " ") + if err != nil { + return fmt.Errorf("marshaling spec: %w", err) + } + if err := os.WriteFile(specPath, out, 0o600); err != nil { + return fmt.Errorf("writing %q: %w", specPath, err) + } + return nil +} + +// defaultKataMounts mirrors the mount set `ctr run --runtime io.containerd.kata.v2` +// produces (the proven-good shape for the kata agent). +func defaultKataMounts() []specs.Mount { + return []specs.Mount{ + {Destination: "/proc", Type: "proc", Source: "proc", Options: []string{"nosuid", "noexec", "nodev"}}, + {Destination: "/dev", Type: "tmpfs", Source: "tmpfs", Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}}, + {Destination: "/dev/pts", Type: "devpts", Source: "devpts", Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}}, + {Destination: "/dev/shm", Type: "tmpfs", Source: "shm", Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}}, + {Destination: "/dev/mqueue", Type: "mqueue", Source: "mqueue", Options: []string{"nosuid", "noexec", "nodev"}}, + {Destination: "/sys", Type: "sysfs", Source: "sysfs", Options: []string{"nosuid", "noexec", "nodev", "ro"}}, + {Destination: "/run", Type: "tmpfs", Source: "tmpfs", Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}}, + } +} + +// defaultKataResources mirrors the device allowlist + cpu shares that +// `ctr run --runtime io.containerd.kata.v2` emits (the proven-good shape). +func defaultKataResources() *specs.LinuxResources { + dev := func(t string, major, minor int64, access string) specs.LinuxDeviceCgroup { + d := specs.LinuxDeviceCgroup{Allow: true, Type: t, Access: access} + if major != 0 { + d.Major = &major + } + if minor >= 0 { + d.Minor = &minor + } + return d + } + shares := uint64(1024) + return &specs.LinuxResources{ + Devices: []specs.LinuxDeviceCgroup{ + {Allow: false, Access: "rwm"}, + dev("c", 1, 3, "rwm"), // /dev/null + dev("c", 1, 8, "rwm"), // /dev/random + dev("c", 1, 7, "rwm"), // /dev/full + dev("c", 5, 0, "rwm"), // /dev/tty + dev("c", 1, 5, "rwm"), // /dev/zero + dev("c", 1, 9, "rwm"), // /dev/urandom + dev("c", 5, 1, "rwm"), // /dev/console + dev("c", 136, -1, "rwm"), // pts + dev("c", 5, 2, "rwm"), // /dev/ptmx + }, + CPU: &specs.LinuxCPU{Shares: &shares}, + } +} + +// CheckpointWorkload suspends the actor and writes a portable CH snapshot. +// +// Contract with atelet (mirrors ateom-gvisor): after we return, atelet uploads +// the checkpoint dir to object storage, then tears down bundles and resets the +// actor dir. +// +// ateom drives CH's REST api-socket (the one kata created at CLHSocketPath(id)): +// pause -> snapshot file:// (config.json + state.json + +// sparse memory-ranges) -> tear the sandbox down (shim + VMM). Driving CH's +// REST API under a kata-owned VMM does not corrupt shim state. +func (s *AteomService) CheckpointWorkload(ctx context.Context, req *ateompb.CheckpointWorkloadRequest) (*ateompb.CheckpointWorkloadResponse, error) { + s.lock.Lock() + defer s.lock.Unlock() + + ns := req.GetActorTemplateNamespace() + name := req.GetActorTemplateName() + id := req.GetActorId() + + // kata-shim-owned actors expose CH at kata's socket; restored (ateom-owned) + // actors at the socket we launched the VMM on. + ra := s.running[id] + chSocket := kata.CLHSocketPath(id) + if ra != nil && ra.apiSocket != "" { + chSocket = ra.apiSocket + } + client := ch.NewClient(chSocket) + if err := client.WaitReady(ctx, 10*time.Second); err != nil { + return nil, fmt.Errorf("while waiting for CH api-socket: %w", err) + } + tPause := time.Now() + if err := client.Pause(ctx); err != nil { + return nil, fmt.Errorf("while pausing guest: %w", err) + } + dPause := time.Since(tPause) + + checkpointDir := ateompath.CheckpointStateDir(ns, name, id) + // Start from a clean dir so CH's snapshot files are the only contents. + if err := os.RemoveAll(checkpointDir); err != nil { + return nil, fmt.Errorf("while clearing checkpoint dir: %w", err) + } + if err := os.MkdirAll(checkpointDir, 0o700); err != nil { + return nil, fmt.Errorf("while creating checkpoint dir: %w", err) + } + + // Capture the virtio-fs shared dir (container rootfs) BEFORE snapshot/ + // teardown — find-paths restore re-opens these by relative path. For a + // kata-shim-owned actor it lives in the shim's mount namespace (nsenter); + // for a restored (ateom-owned) actor we built it ourselves in our own + // namespace (ReconstructSharedDir), so tar it directly. + tCapture := time.Now() + if ra != nil && ra.shim != nil { + if err := ra.shim.CaptureSharedDir(ctx, filepath.Join(checkpointDir, sharedDirTar)); err != nil { + return nil, fmt.Errorf("while capturing virtio-fs shared dir: %w", err) + } + } else if _, statErr := os.Stat(kata.SharedDir(id)); statErr == nil { + if err := kata.CaptureSharedDirLocal(ctx, id, filepath.Join(checkpointDir, sharedDirTar)); err != nil { + return nil, fmt.Errorf("while capturing local shared dir: %w", err) + } + } else { + slog.WarnContext(ctx, "No shared dir found for actor; skipping shared-dir capture", slog.String("id", id)) + } + dCapture := time.Since(tCapture) + + slog.InfoContext(ctx, "Snapshotting guest", slog.String("id", id), slog.String("dir", checkpointDir)) + tSnapshot := time.Now() + if err := client.Snapshot(ctx, checkpointDir); err != nil { + return nil, fmt.Errorf("while snapshotting guest: %w", err) + } + dSnapshot := time.Since(tSnapshot) + + // Report exactly the files we wrote so atelet ships precisely the CH snapshot + // (config.json + state.json + memory-ranges + shared-dir.tar), not gVisor's + // fixed set. + snapshotFiles, err := listFiles(checkpointDir) + if err != nil { + return nil, fmt.Errorf("while listing snapshot files: %w", err) + } + + // Tear down: the actor returns to "available". Best-effort; the snapshot is + // already on disk for atelet to ship. + tTeardown := time.Now() + s.teardownActor(ctx, id, ra, client) + dTeardown := time.Since(tTeardown) + delete(s.running, id) + + // Tear down the per-activation actor network (mirrors gVisor). + if err := s.cleanupActorNetwork(ctx); err != nil { + slog.WarnContext(ctx, "Failed to clean up actor network after checkpoint", slog.Any("err", err)) + } + + slog.InfoContext(ctx, "Actor checkpointed", slog.String("id", id), slog.Any("snapshot_files", snapshotFiles), + slog.Duration("pause", dPause), slog.Duration("capture", dCapture), + slog.Duration("snapshot", dSnapshot), slog.Duration("teardown", dTeardown)) + return &ateompb.CheckpointWorkloadResponse{SnapshotFiles: snapshotFiles}, nil +} + +// listFiles returns the (relative) names of regular files directly under dir. +func listFiles(dir string) ([]string, error) { + entries, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + var files []string + for _, e := range entries { + if e.Type().IsRegular() { + files = append(files, e.Name()) + } + } + return files, nil +} + +// teardownActor stops the CH VMM and the kata shim for an actor. Best-effort: +// the VM may be paused (post-snapshot), so shutting the VMM down via the CH API +// is the reliable stop; the shim teardown then cleans up sandbox state. ra may +// be nil (e.g. ateom restarted and lost in-memory state). +func (s *AteomService) teardownActor(ctx context.Context, id string, ra *runningActor, client *ch.Client) { + if client != nil { + tShutdown := time.Now() + shutCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + if err := client.Shutdown(shutCtx); err != nil { + slog.WarnContext(ctx, "CH shutdown failed (continuing teardown)", slog.Any("err", err)) + } + cancel() + slog.InfoContext(ctx, "CH API shutdown done", slog.Duration("took", time.Since(tShutdown))) + } + if ra == nil { + return + } + // kata-shim-owned: stop the shim (which owns the CH process) + clean up. + if ra.shim != nil { + shutCtx, cancel := context.WithTimeout(ctx, 15*time.Second) + if err := ra.shim.Shutdown(shutCtx, true); err != nil { + slog.WarnContext(ctx, "shim shutdown failed (continuing teardown)", slog.Any("err", err)) + } + cancel() + _ = ra.shim.Close() + clCtx, cancel2 := context.WithTimeout(ctx, 15*time.Second) + if err := ra.shim.CleanupAction(clCtx); err != nil { + slog.WarnContext(ctx, "shim cleanup failed", slog.Any("err", err)) + } + cancel2() + } + // ateom-owned (post-restore): kill the CH + virtiofsd we launched. + if ra.chCmd != nil && ra.chCmd.Process != nil { + _ = ra.chCmd.Process.Kill() + _, _ = ra.chCmd.Process.Wait() + } + if ra.vfsdCmd != nil && ra.vfsdCmd.Process != nil { + _ = ra.vfsdCmd.Process.Kill() + _, _ = ra.vfsdCmd.Process.Wait() + } +} + +// RestoreWorkload restores the actor on a (possibly different) pod by relaunching +// cloud-hypervisor directly from the downloaded snapshot — bypassing the kata +// shim — and resuming. +// +// Contract with atelet: the snapshot dir (config.json + state.json + +// memory-ranges + shared-dir.tar) has been downloaded to RestoreStateDir. +// +// Steps: +// 1. Reconstruct the virtio-fs shared dir (container rootfs) from shared-dir.tar +// so find-paths can re-open the inodes the snapshot references. +// 2. Start virtiofsd on the vhost-user socket the snapshot expects. +// 3. Relaunch CH with --restore source_url=file:// (ch.Restore), which +// reconnects to virtiofsd, recreates vsock, and reloads guest RAM (paused). +// 4. Resume. +// +// The snapshot's config.json references kernel + guest-OS image at static node +// paths (present on any kata node) and per-sandbox sockets under VMDir(id); this +// POC restores under the same id, so those paths line up. ateom then owns the CH. +func (s *AteomService) RestoreWorkload(ctx context.Context, req *ateompb.RestoreWorkloadRequest) (resp *ateompb.RestoreWorkloadResponse, retErr error) { + s.lock.Lock() + defer s.lock.Unlock() + + ns := req.GetActorTemplateNamespace() + name := req.GetActorTemplateName() + id := req.GetActorId() + + restoreDir := ateompath.RestoreStateDir(ns, name, id) + + // Resolve the cloud-hypervisor + virtiofsd binaries (fetched assets or flags). + rr, err := s.resolveRuntime(ateompath.ActorPath(ns, name, id), req.GetRuntimeAssetPaths()) + if err != nil { + return nil, fmt.Errorf("while resolving runtime assets: %w", err) + } + + // Clear leftover per-sandbox state/processes from prior failed attempts + // (stale virtiofsd holding the vhost-user socket, etc.). + kata.CleanupSandboxState(id) + + // The snapshot was taken under the SOURCE actor's id (e.g. the golden + // actor); its config.json references that id's socket paths. Rewrite them to + // this actor's VMDir so the sockets we create are the ones CH reopens. + if err := rewriteSnapshotSocketPaths(restoreDir, id); err != nil { + return nil, fmt.Errorf("while rewriting snapshot socket paths: %w", err) + } + + // 1. Reconstruct the virtio-fs shared dir from the shipped tar. + tarPath := filepath.Join(restoreDir, sharedDirTar) + if _, err := os.Stat(tarPath); err != nil { + return nil, fmt.Errorf("missing shared-dir tar %q: %w", tarPath, err) + } + if err := kata.ReconstructSharedDir(ctx, tarPath, id); err != nil { + return nil, fmt.Errorf("while reconstructing shared dir: %w", err) + } + + // kata's per-sandbox runtime dir holds the sockets the snapshot references. + if err := os.MkdirAll(kata.VMDir(id), 0o700); err != nil { + return nil, fmt.Errorf("while creating VM dir: %w", err) + } + + // 2. Networking: rebuild the per-activation actor veth, then recreate + // kata's tap + TC mirror against it. The snapshot's virtio-net device is + // fd-backed, so CH requires fresh tap FDs on restore (net_fds). The guest's + // frozen network config (stable actor address, gateway with a fixed MAC) + // remains valid as-is — no in-guest reconfiguration. + if err := s.setupActorNetwork(ctx); err != nil { + return nil, fmt.Errorf("while setting up actor network: %w", err) + } + defer func() { + if retErr != nil { + if cleanupErr := s.cleanupActorNetwork(ctx); cleanupErr != nil { + slog.WarnContext(ctx, "Failed to clean up actor network after Restore failure", slog.Any("err", cleanupErr)) + } + } + }() + netDevs, err := ch.SnapshotNetDevices(restoreDir) + if err != nil { + return nil, fmt.Errorf("while reading snapshot net devices: %w", err) + } + var restoredNets []ch.RestoredNet + var tapFiles []*os.File + defer func() { + // CH dups the FDs it adopts (SCM_RIGHTS), so ours close unconditionally. + for _, f := range tapFiles { + _ = f.Close() + } + }() + for i, nd := range netDevs { + files, terr := s.setupRestoreTap(ctx, fmt.Sprintf("tap%d_kata", i), nd.QueuePairs) + if terr != nil { + return nil, fmt.Errorf("while building restore tap for %s: %w", nd.ID, terr) + } + tapFiles = append(tapFiles, files...) + rn := ch.RestoredNet{ID: nd.ID} + for _, f := range files { + rn.FDs = append(rn.FDs, int(f.Fd())) + } + restoredNets = append(restoredNets, rn) + } + + // 3. Start virtiofsd on the vhost-user socket from config.json. + vfsdCmd, err := kata.StartVirtiofsd(ctx, kata.VirtiofsdOptions{ + Binary: rr.virtiofsd, + SocketPath: kata.VirtiofsdSocketPath(id), + SharedDir: kata.SharedDir(id), + Log: slogWriter{ctx}, + }) + if err != nil { + return nil, fmt.Errorf("while starting virtiofsd: %w", err) + } + defer func() { + if retErr != nil && vfsdCmd.Process != nil { + _ = vfsdCmd.Process.Kill() + } + }() + + // 4. Launch a bare VMM and restore with the tap FDs attached (SCM_RIGHTS). + apiSocket := filepath.Join(kata.VMDir(id), "clh-api-restore.sock") + slog.InfoContext(ctx, "Restoring CH from snapshot", slog.String("id", id), slog.String("dir", restoreDir)) + chCmd, client, err := ch.LaunchVMM(ctx, ch.LaunchVMMOptions{ + Binary: rr.ch, + APISocket: apiSocket, + Stdout: slogWriter{ctx}, + Stderr: slogWriter{ctx}, + }) + if err != nil { + return nil, fmt.Errorf("while launching VMM for restore: %w", err) + } + defer func() { + if retErr != nil && chCmd.Process != nil { + _ = chCmd.Process.Kill() + } + }() + if err := client.RestoreWithNetFDs(ctx, restoreDir, restoredNets); err != nil { + return nil, fmt.Errorf("while restoring VM with net FDs: %w", err) + } + + // 5. Resume (restore comes back paused). + if err := client.Resume(ctx); err != nil { + return nil, fmt.Errorf("while resuming restored guest: %w", err) + } + + s.running[id] = &runningActor{chCmd: chCmd, vfsdCmd: vfsdCmd, apiSocket: apiSocket} + slog.InfoContext(ctx, "Actor restored", slog.String("id", id)) + return &ateompb.RestoreWorkloadResponse{}, nil +} + +// rewriteSnapshotSocketPaths updates a CH snapshot config.json's per-sandbox +// socket paths (virtio-fs vhost-user socket, hybrid-vsock socket) from the +// source actor's VMDir to the restoring actor's VMDir. Disk/kernel paths are +// content-addressed static files and identical on every node. +func rewriteSnapshotSocketPaths(snapshotDir, id string) error { + cfgPath := filepath.Join(snapshotDir, "config.json") + b, err := os.ReadFile(cfgPath) + if err != nil { + return err + } + var cfg map[string]any + if err := json.Unmarshal(b, &cfg); err != nil { + return fmt.Errorf("parsing %q: %w", cfgPath, err) + } + if fsList, ok := cfg["fs"].([]any); ok { + for _, f := range fsList { + if fm, ok := f.(map[string]any); ok { + fm["socket"] = kata.VirtiofsdSocketPath(id) + } + } + } + if vsock, ok := cfg["vsock"].(map[string]any); ok { + vsock["socket"] = kata.VsockSocketPath(id) + } + out, err := json.Marshal(cfg) + if err != nil { + return err + } + return os.WriteFile(cfgPath, out, 0o600) +} + +// slogWriter adapts an io.Writer to slog at info level, for the kata shim's +// start/delete diagnostics. +type slogWriter struct{ ctx context.Context } + +func (w slogWriter) Write(p []byte) (int, error) { + slog.InfoContext(w.ctx, "kata shim", slog.String("out", string(p))) + return len(p), nil +} diff --git a/cmd/ateom-cloud-hypervisor/service_integration_test.go b/cmd/ateom-cloud-hypervisor/service_integration_test.go new file mode 100644 index 000000000..a217a6507 --- /dev/null +++ b/cmd/ateom-cloud-hypervisor/service_integration_test.go @@ -0,0 +1,216 @@ +//go:build linux + +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/agent-substrate/substrate/cmd/ateom-cloud-hypervisor/internal/ch" + "github.com/agent-substrate/substrate/cmd/ateom-cloud-hypervisor/internal/kata" + "github.com/agent-substrate/substrate/internal/ateompath" + "github.com/agent-substrate/substrate/internal/proto/ateompb" + "golang.org/x/sys/unix" +) + +// TestServiceE2E drives the REAL AteomService gRPC handlers (RunWorkload, +// CheckpointWorkload, RestoreWorkload) against kata + cloud-hypervisor, with +// an atelet-style OCI bundle and the object-storage round trip +// simulated by copying CheckpointStateDir -> RestoreStateDir. This proves the +// whole ateom-cloud-hypervisor service works end-to-end (not just the helpers): +// path derivation, ensureKataCompatibleSpec (the bundle here is a minimal +// gVisor-style spec with no linux.resources), the running map, shared-dir +// capture/reconstruct, virtiofsd relaunch, CH restore, and teardown. +// +// Gated behind KATA_INTEGRATION=1. Env: KATA_ROOTFS_SRC (required), +// KATA_SHIM / KATA_CH / KATA_VIRTIOFSD (sensible defaults provided). +func TestServiceE2E(t *testing.T) { + if os.Getenv("KATA_INTEGRATION") != "1" { + t.Skip("set KATA_INTEGRATION=1 to run (requires kata + /dev/kvm + root)") + } + rootfsSrc := os.Getenv("KATA_ROOTFS_SRC") + if rootfsSrc == "" { + t.Fatal("KATA_ROOTFS_SRC is required") + } + shim := envOrTest("KATA_SHIM", "/opt/kata/bin/containerd-shim-kata-v2") + chBin := envOrTest("KATA_CH", "/usr/local/bin/cloud-hypervisor") + vfsdBin := envOrTest("KATA_VIRTIOFSD", "/usr/local/bin/virtiofsd-patched") + + ns, name := "default", "e2e" + id := fmt.Sprintf("ateomchv-svc-%d", os.Getpid()) + container := "app" + + // --- atelet-style bundle prep at the ateompath the service expects. --- + bundle := ateompath.OCIBundlePath(ns, name, id, container) + rootfs := filepath.Join(bundle, "rootfs") + if err := os.MkdirAll(rootfs, 0o755); err != nil { + t.Fatal(err) + } + if out, err := exec.Command("cp", "-a", rootfsSrc+"/.", rootfs+"/").CombinedOutput(); err != nil { + t.Fatalf("copying rootfs: %v: %s", err, out) + } + writeMinimalGvisorStyleSpec(t, bundle) // no linux.resources -> exercises ensureKataCompatibleSpec + drainBundleLog(t, bundle) // visibility into shim logs if anything fails + + svc := NewService("testpod", shim, chBin, vfsdBin, "", "default", true, -1) + ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second) + defer cancel() + + t.Cleanup(func() { + cctx, c := context.WithTimeout(context.Background(), 20*time.Second) + svc.teardownActor(cctx, id, svc.running[id], nil) + c() + _ = os.RemoveAll(ateompath.ActorPath(ns, name, id)) + _ = os.RemoveAll(kata.VMDir(id)) + _ = os.RemoveAll(kata.SharedDir(id)) + }) + + // --- RunWorkload. --- + if _, err := svc.RunWorkload(ctx, &ateompb.RunWorkloadRequest{ + ActorTemplateNamespace: ns, ActorTemplateName: name, ActorId: id, + Spec: &ateompb.WorkloadSpec{Containers: []*ateompb.Container{{Name: container}}}, + }); err != nil { + t.Fatalf("RunWorkload: %v", err) + } + t.Log("RunWorkload OK (micro-VM booted via kata)") + + // --- CheckpointWorkload. --- + if _, err := svc.CheckpointWorkload(ctx, &ateompb.CheckpointWorkloadRequest{ + ActorTemplateNamespace: ns, ActorTemplateName: name, ActorId: id, + Spec: &ateompb.WorkloadSpec{Containers: []*ateompb.Container{{Name: container}}}, + }); err != nil { + t.Fatalf("CheckpointWorkload: %v", err) + } + checkpointDir := ateompath.CheckpointStateDir(ns, name, id) + for _, f := range []string{"config.json", "state.json", "memory-ranges", "shared-dir.tar"} { + if _, err := os.Stat(filepath.Join(checkpointDir, f)); err != nil { + t.Fatalf("checkpoint missing %q: %v", f, err) + } + } + t.Log("CheckpointWorkload OK (snapshot + shared-dir.tar written)") + + // --- Simulate atelet's object-storage round trip. --- + restoreDir := ateompath.RestoreStateDir(ns, name, id) + if err := os.MkdirAll(restoreDir, 0o700); err != nil { + t.Fatal(err) + } + if out, err := exec.Command("cp", "-a", checkpointDir+"/.", restoreDir+"/").CombinedOutput(); err != nil { + t.Fatalf("shipping snapshot: %v: %s", err, out) + } + t.Log("shipped snapshot CheckpointStateDir -> RestoreStateDir") + + // --- RestoreWorkload (fresh CH process, ateom-owned). --- + if _, err := svc.RestoreWorkload(ctx, &ateompb.RestoreWorkloadRequest{ + ActorTemplateNamespace: ns, ActorTemplateName: name, ActorId: id, + Spec: &ateompb.WorkloadSpec{Containers: []*ateompb.Container{{Name: container}}}, + }); err != nil { + t.Fatalf("RestoreWorkload: %v", err) + } + t.Log("RestoreWorkload OK") + + // --- Liveness: the restored guest must be live on its new CH. --- + client := ch.NewClient(filepath.Join(kata.VMDir(id), "clh-api-restore.sock")) + if err := client.WaitReady(ctx, 10*time.Second); err != nil { + t.Fatalf("restored CH not ready: %v", err) + } + if err := client.Pause(ctx); err != nil { + t.Errorf("post-restore Pause (liveness): %v", err) + } + if err := client.Resume(ctx); err != nil { + t.Errorf("post-restore Resume (liveness): %v", err) + } + t.Log("E2E OK: actor ran, checkpointed to 'storage', and restored live on a fresh CH process") +} + +func envOrTest(key, def string) string { + if v := os.Getenv(key); v != "" { + return v + } + return def +} + +// writeMinimalGvisorStyleSpec writes a deliberately minimal OCI spec (no +// linux.resources / cgroupsPath) so the test exercises ensureKataCompatibleSpec. +func writeMinimalGvisorStyleSpec(t *testing.T, bundle string) { + t.Helper() + spec := map[string]any{ + "ociVersion": "1.0.2", + "process": map[string]any{ + "user": map[string]any{"uid": 0, "gid": 0}, + "args": []string{"sleep", "3600"}, + "env": []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, + "cwd": "/", + "capabilities": map[string]any{ + "bounding": []string{"CAP_KILL", "CAP_AUDIT_WRITE", "CAP_NET_BIND_SERVICE"}, + "effective": []string{"CAP_KILL", "CAP_AUDIT_WRITE", "CAP_NET_BIND_SERVICE"}, + "permitted": []string{"CAP_KILL", "CAP_AUDIT_WRITE", "CAP_NET_BIND_SERVICE"}, + }, + }, + "root": map[string]any{"path": "rootfs", "readonly": false}, + "hostname": "ateomchv", + "mounts": []map[string]any{ + {"destination": "/proc", "type": "proc", "source": "proc"}, + {"destination": "/dev", "type": "tmpfs", "source": "tmpfs"}, + {"destination": "/sys", "type": "sysfs", "source": "sysfs", "options": []string{"nosuid", "noexec", "nodev", "ro"}}, + }, + "linux": map[string]any{ + "namespaces": []map[string]any{ + {"type": "pid"}, {"type": "network"}, {"type": "ipc"}, {"type": "uts"}, {"type": "mount"}, + }, + }, + } + b, err := json.MarshalIndent(spec, "", " ") + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(bundle, "config.json"), b, 0o600); err != nil { + t.Fatal(err) + } +} + +// drainBundleLog creates the kata shim's "log" fifo and drains it to t.Log. +func drainBundleLog(t *testing.T, bundle string) { + t.Helper() + logFifo := filepath.Join(bundle, "log") + _ = os.Remove(logFifo) + if err := unix.Mkfifo(logFifo, 0o700); err != nil { + t.Fatalf("mkfifo log: %v", err) + } + go func() { + f, err := os.OpenFile(logFifo, os.O_RDONLY, 0) + if err != nil { + return + } + defer f.Close() + buf := make([]byte, 4096) + for { + n, err := f.Read(buf) + if n > 0 { + t.Logf("[shim-log] %s", buf[:n]) + } + if err != nil { + return + } + } + }() +} diff --git a/demos/counter/counter-microvm.yaml.tmpl b/demos/counter/counter-microvm.yaml.tmpl new file mode 100644 index 000000000..7c46d6df7 --- /dev/null +++ b/demos/counter/counter-microvm.yaml.tmpl @@ -0,0 +1,90 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Micro-VM (kata + cloud-hypervisor) variant of the counter demo. The counter is +# in-RAM (atomic uint64), so a successful suspend/resume across pods shows the +# count continuing — proving the guest memory snapshot round-tripped. +# +# Runtime assets (kata shim, cloud-hypervisor, patched virtiofsd, guest kernel, +# guest rootfs, base configuration.toml) are FETCHED at runtime from the cluster +# object store (rustfs bucket ${BUCKET_NAME}, prefix kata-assets/) — NOT baked +# into the worker image. The sha256 values below are the arm64 asset set +# produced by hack/microvm-assets/assemble.sh; for another architecture, +# assemble that arch's assets and replace the hashes. authentication.gcp routes the fetch to atelet's main +# (rustfs/S3) client rather than the anonymous public-GCS client. + +apiVersion: v1 +kind: Namespace +metadata: + name: ate-demo-counter-microvm + +--- + +apiVersion: ate.dev/v1alpha1 +kind: WorkerPool +metadata: + name: counter-microvm + namespace: ate-demo-counter-microvm +spec: + replicas: 2 + runtime: microvm + ateomImage: ko://github.com/agent-substrate/substrate/cmd/ateom-cloud-hypervisor + +--- + +apiVersion: ate.dev/v1alpha1 +kind: ActorTemplate +metadata: + name: counter-microvm + namespace: ate-demo-counter-microvm +spec: + # Runsc is unused for the micro-VM runtime but the field is currently required; + # leave a valid reference so validation passes. + runsc: + amd64: + url: "gs://gvisor/releases/nightly/2026-05-19/x86_64/runsc" + sha256Hash: "a397be1abc2420d26bce6c70e6e2ff96c73aaaab929756c56f5e2089ea842b63" + runtime: + type: microvm + authentication: + gcp: {} + assets: + kata-shim: + url: "gs://${BUCKET_NAME}/kata-assets/containerd-shim-kata-v2" + sha256Hash: "3ec6dc6aa1aa326d5bdfac9d6c9dae8d34642f4ef3adddfe06c27564e640c36a" + cloud-hypervisor: + url: "gs://${BUCKET_NAME}/kata-assets/cloud-hypervisor" + sha256Hash: "bf004ddc1a148f47caa87ac49a783b8dbd6bf9bc27abe522ed197df7b982d3b1" + virtiofsd: + url: "gs://${BUCKET_NAME}/kata-assets/virtiofsd-patched" + sha256Hash: "60447339d8070c7de2dc2bea6bdd483b1bdb7a7b7ddff04b5d80bad39578b1c2" + kata-kernel: + url: "gs://${BUCKET_NAME}/kata-assets/vmlinux" + sha256Hash: "a44d663f4ddad20a35527a3578fadef9beb23c1e5cb720e85d6928d6de70d3a1" + kata-image: + url: "gs://${BUCKET_NAME}/kata-assets/rootfs.img" + sha256Hash: "7ebd652760c881374c0a761d34addcb76d9a650e35c10c01b780ebcdd9a1f2aa" + kata-config: + url: "gs://${BUCKET_NAME}/kata-assets/configuration-clh.toml" + sha256Hash: "df504d9be0ed01765fdc8a9467955e1e671eb97724443f65a524bf914ccb818b" + pauseImage: "registry.k8s.io/pause:3.10.2@sha256:f548e0e8e3dc1896ca956272154dde3314e8cc4fde0a57577ee9fa1c63f5baf4" + containers: + - name: counter + image: ko://github.com/agent-substrate/substrate/demos/counter + command: ["/ko-app/counter"] + workerPoolRef: + namespace: ate-demo-counter-microvm + name: counter-microvm + snapshotsConfig: + location: gs://${BUCKET_NAME}/ate-demo-counter-microvm/ diff --git a/go.mod b/go.mod index 16b41313f..6a83e4594 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ require ( github.com/aws/aws-sdk-go-v2 v1.41.7 github.com/aws/aws-sdk-go-v2/config v1.32.17 github.com/aws/aws-sdk-go-v2/service/s3 v1.101.0 + github.com/containerd/containerd/api v1.11.1 + github.com/containerd/ttrpc v1.2.8 github.com/envoyproxy/go-control-plane v0.14.0 github.com/envoyproxy/go-control-plane/envoy v1.37.0 github.com/google/go-cmp v0.7.0 @@ -81,6 +83,7 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect diff --git a/go.sum b/go.sum index 2151f0ae3..f7acbf349 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,14 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= +github.com/containerd/containerd/api v1.11.1 h1:h8nfoDW9+fNsC/9TwiAHj8B1GzXKtR4eFtkhi/X5RLU= +github.com/containerd/containerd/api v1.11.1/go.mod h1:CaQFRu+N1MtbgL6JDOJLUB1hCKESU1lD6MuTJhgtdlw= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= +github.com/containerd/ttrpc v1.2.8 h1:xbVu6D4qF2jihdh9rDVOKqUMiFBQk6YctTdo1zk087Y= +github.com/containerd/ttrpc v1.2.8/go.mod h1:wyZW2K79t4Hfcxl+GUvkZqRBzJlqFFvgEeeWXa42tyE= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/hack/create-kind-cluster.sh b/hack/create-kind-cluster.sh index 2915a95a2..bce81d464 100755 --- a/hack/create-kind-cluster.sh +++ b/hack/create-kind-cluster.sh @@ -42,13 +42,38 @@ if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true registry:3 fi -# 2. Create kind configuration with containerdConfigPatches and feature gates +# 2. Create kind configuration with containerdConfigPatches and feature gates. +# +# Probe for /dev/kvm where the kind nodes will actually run — inside the Docker +# provider VM on macOS (Lima/Colima/Docker Desktop), the host itself on Linux — +# and only wire up micro-VM (kata + cloud-hypervisor) support when present. +# Without KVM the cluster still works for gVisor. +echo "Probing for /dev/kvm in the Docker environment..." +HAS_KVM=0 +if docker run --rm --device /dev/kvm busybox true >/dev/null 2>&1; then + HAS_KVM=1 + echo "/dev/kvm found: micro-VM (kata + cloud-hypervisor) support will be enabled." +else + echo "/dev/kvm not available: micro-VM support disabled (gVisor still works)." +fi + echo "Creating kind configuration for cluster '${KIND_CLUSTER_NAME}'..." cat < "${ROOT}/bin/kind-config.yaml" kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane +EOF +if [ "${HAS_KVM}" = "1" ]; then + cat <> "${ROOT}/bin/kind-config.yaml" + # Bind-mount /dev/kvm into the node so micro-VM (kata + cloud-hypervisor) + # worker pods can use KVM. + extraMounts: + - hostPath: /dev/kvm + containerPath: /dev/kvm +EOF +fi +cat <> "${ROOT}/bin/kind-config.yaml" # cmd/podcertcontroller depends on ClusterTrustBundle & PodCertificateRequest. # They are not enabled by default as of Kubernetes v1.36 # https://github.com/kubernetes/kubernetes/blob/master/test/compatibility_lifecycle/reference/versioned_feature_list.yaml @@ -72,6 +97,16 @@ for node in $("${ROOT}"/hack/kind.sh get nodes --name "${KIND_CLUSTER_NAME}"); d docker exec "${node}" sysctl net.ipv4.conf.all.proxy_arp=1 done +# 2.6 When KVM is available: make /dev/kvm usable inside the node and label +# nodes so micro-VM WorkerPools (nodeSelector ate.dev/runtime=microvm) schedule. +if [ "${HAS_KVM}" = "1" ]; then + echo "Preparing kind nodes for micro-VM (kata + cloud-hypervisor) runtime..." + for node in $("${ROOT}"/hack/kind.sh get nodes --name "${KIND_CLUSTER_NAME}"); do + docker exec "${node}" chmod 666 /dev/kvm + kubectl label node "${node}" ate.dev/runtime=microvm --overwrite + done +fi + # 3. Add the registry config to the nodes echo "Adding registry config to kind nodes..." REGISTRY_DIR="/etc/containerd/certs.d/localhost:${reg_port}" diff --git a/hack/microvm-assets/README.md b/hack/microvm-assets/README.md new file mode 100644 index 000000000..b18fa216f --- /dev/null +++ b/hack/microvm-assets/README.md @@ -0,0 +1,43 @@ +# Micro-VM runtime assets + counter demo (kind, fetch-not-bake) + +The `microvm` runtime (`cmd/ateom-cloud-hypervisor`, kata + cloud-hypervisor) fetches its +toolchain at runtime — nothing kata-specific is baked into the worker image. These helpers +assemble the asset set for your node arch, stage it into the cluster's rustfs S3 bucket, and the +demo manifest points the ActorTemplate at it. + +The worker image base is `debian:stable-slim` (provides `nsenter` + `tar` + glibc for the fetched +Rust binaries); see `.ko.yaml`. When `/dev/kvm` is available, `hack/create-kind-cluster.sh` +mounts it into the node and labels the node `ate.dev/runtime=microvm`. + +## Steps (run on a KVM-capable Linux host matching the node arch) + +1. **Assemble assets for your arch** (builds the patched virtiofsd — needs rust via rustup, NOT + distro cargo which is too old for the v4 lockfile, plus build deps: + `apt-get install -y git libcap-ng-dev libseccomp-dev pkg-config && curl -fsSL https://sh.rustup.rs | sh -s -- -y`): + ```sh + ARCH=arm64 hack/microvm-assets/assemble.sh + ``` + Copy the printed sha256 sums into `demos/counter/counter-microvm.yaml.tmpl` `runtime.assets` + (the committed values are arm64; other arches differ). + +2. **Bring up the cluster + control plane:** + ```sh + hack/create-kind-cluster.sh # mounts /dev/kvm, labels node ate.dev/runtime=microvm + hack/install-ate-kind.sh # control plane + rustfs (bucket: ate-snapshots) + ``` + +3. **Stage assets into rustfs:** + ```sh + OUT="$PWD/microvm-assets-arm64" hack/microvm-assets/stage-to-rustfs.sh + ``` + +4. **Apply the demo + drive it:** + ```sh + BUCKET_NAME=ate-snapshots envsubst < demos/counter/counter-microvm.yaml.tmpl | kubectl apply -f - + ``` + Create an actor from `counter-microvm`, hit the in-RAM counter to increment it, suspend + (checkpoint), resume on a different worker pod, and confirm the count continues — proving the + guest-memory snapshot round-tripped across pods. + +## Notes +- `assets` is single-arch (unlike runsc's amd64/arm64): stage assets matching the node arch. diff --git a/hack/microvm-assets/assemble.sh b/hack/microvm-assets/assemble.sh new file mode 100755 index 000000000..a136b4f8b --- /dev/null +++ b/hack/microvm-assets/assemble.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Assemble the micro-VM (kata + cloud-hypervisor) runtime asset set that +# ateom-cloud-hypervisor fetches at runtime (fetch-not-bake). Run this on a Linux +# host of the TARGET arch. +# +# Produces, under $OUT, the six assets named as the ActorTemplate expects, plus +# their sha256 sums (paste into demos/counter/counter-microvm.yaml.tmpl): +# containerd-shim-kata-v2 cloud-hypervisor virtiofsd-patched +# vmlinux rootfs.img configuration-clh.toml +# +# virtiofsd is BUILT from source with the vhost-0.16 bump (stock virtiofsd breaks +# CH snapshot/restore; the bumped vhost crates carry the REPLY_ACK fix). Build deps on +# Debian/Ubuntu: apt-get install -y git libcap-ng-dev libseccomp-dev pkg-config +# Rust: use rustup (current stable). Distro cargo (e.g. apt's 1.75) is too old — +# virtiofsd's Cargo.lock is lockfile v4, which needs cargo >= 1.78: +# curl -fsSL https://sh.rustup.rs | sh -s -- -y && . "$HOME/.cargo/env" +# +# Env: ARCH (arm64|amd64, default arm64), KATA_VER (3.31.0), CH_VER (v52.0), +# VIRTIOFSD_REF (upstream commit; default b6d2aaa), OUT (default ./microvm-assets-$ARCH). + +set -o errexit -o nounset -o pipefail + +ARCH="${ARCH:-arm64}" +KATA_VER="${KATA_VER:-3.31.0}" +CH_VER="${CH_VER:-v52.0}" +VIRTIOFSD_REF="${VIRTIOFSD_REF:-b6d2aaa}" +OUT="${OUT:-$PWD/microvm-assets-$ARCH}" +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +case "$ARCH" in + arm64) CH_ASSET="cloud-hypervisor-static-aarch64" ;; + amd64) CH_ASSET="cloud-hypervisor-static" ;; + *) echo "unsupported ARCH=$ARCH" >&2; exit 1 ;; +esac + +mkdir -p "$OUT" +cd "$WORK" + +echo ">> Downloading kata-static ${KATA_VER} (${ARCH})..." +curl -fSL -o kata-static.tar.zst \ + "https://github.com/kata-containers/kata-containers/releases/download/${KATA_VER}/kata-static-${KATA_VER}-${ARCH}.tar.zst" +mkdir -p kata +tar --zstd -xf kata-static.tar.zst -C kata +KROOT="kata/opt/kata" + +cp "${KROOT}/bin/containerd-shim-kata-v2" "${OUT}/containerd-shim-kata-v2" +cp "$(readlink -f "${KROOT}/share/kata-containers/vmlinux.container")" "${OUT}/vmlinux" +cp "$(readlink -f "${KROOT}/share/kata-containers/kata-containers.img")" "${OUT}/rootfs.img" +cp "${KROOT}/share/defaults/kata-containers/configuration-clh.toml" "${OUT}/configuration-clh.toml" + +echo ">> Downloading cloud-hypervisor ${CH_VER} (${CH_ASSET})..." +curl -fSL -o "${OUT}/cloud-hypervisor" \ + "https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/${CH_VER}/${CH_ASSET}" +chmod +x "${OUT}/cloud-hypervisor" + +echo ">> Building patched virtiofsd (vhost 0.16) from ${VIRTIOFSD_REF}..." +if ! command -v cargo >/dev/null 2>&1; then + echo "cargo not found; install rust + build deps (see header)" >&2 + exit 1 +fi +git clone https://gitlab.com/virtio-fs/virtiofsd.git +( + cd virtiofsd + git checkout "${VIRTIOFSD_REF}" + # Bump the vhost crates to the versions that contain rust-vmm/vhost@14db3cd + # (the snapshot/restore REPLY_ACK fix). Robust to the exact pinned versions. + sed -i -E 's/^vhost-user-backend = "[^"]*"/vhost-user-backend = "0.22"/' Cargo.toml + sed -i -E 's/^vhost = "[^"]*"/vhost = "0.16"/' Cargo.toml + grep -E '^(vhost|vhost-user-backend) =' Cargo.toml + cargo build --release +) +cp "virtiofsd/target/release/virtiofsd" "${OUT}/virtiofsd-patched" +chmod +x "${OUT}/virtiofsd-patched" + +echo +echo ">> Assets assembled in ${OUT}:" +cd "${OUT}" +for f in containerd-shim-kata-v2 cloud-hypervisor virtiofsd-patched vmlinux rootfs.img configuration-clh.toml; do + [ -f "$f" ] || { echo "MISSING: $f" >&2; exit 1; } +done +"${OUT}/virtiofsd-patched" --version 2>/dev/null | head -1 || true +echo +echo ">> sha256 (paste into demos/counter/counter-microvm.yaml.tmpl runtime.assets):" +sha256sum containerd-shim-kata-v2 cloud-hypervisor virtiofsd-patched vmlinux rootfs.img configuration-clh.toml diff --git a/hack/microvm-assets/stage-to-rustfs.sh b/hack/microvm-assets/stage-to-rustfs.sh new file mode 100755 index 000000000..0d4d47087 --- /dev/null +++ b/hack/microvm-assets/stage-to-rustfs.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Stage the assembled micro-VM asset set into the kind cluster's rustfs S3 bucket +# under kata-assets/, where atelet fetches it (per demos/counter/counter-microvm.yaml.tmpl). +# Run after the cluster is up (hack/install-ate-kind.sh) and assemble.sh has produced $OUT. +# +# Requires the `aws` CLI. Env: OUT (asset dir, default ./microvm-assets-arm64), +# BUCKET (default ate-snapshots), NAMESPACE (rustfs namespace, default ate-system). + +set -o errexit -o nounset -o pipefail + +OUT="${OUT:-$PWD/microvm-assets-arm64}" +BUCKET="${BUCKET:-ate-snapshots}" +NAMESPACE="${NAMESPACE:-ate-system}" + +export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-rustfsadmin}" +export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-rustfsadmin}" +export AWS_REGION="${AWS_REGION:-us-east-1}" + +echo ">> Port-forwarding svc/rustfs 9000 in namespace ${NAMESPACE}..." +kubectl -n "${NAMESPACE}" port-forward svc/rustfs 9000:9000 >/tmp/rustfs-pf.log 2>&1 & +PF_PID=$! +trap 'kill "$PF_PID" 2>/dev/null || true' EXIT +sleep 3 + +ENDPOINT="http://localhost:9000" +echo ">> Uploading assets to s3://${BUCKET}/kata-assets/ via ${ENDPOINT}..." +for f in containerd-shim-kata-v2 cloud-hypervisor virtiofsd-patched vmlinux rootfs.img configuration-clh.toml; do + echo " $f" + aws --endpoint-url "${ENDPOINT}" s3 cp "${OUT}/${f}" "s3://${BUCKET}/kata-assets/${f}" +done + +echo ">> Done. Verify:" +aws --endpoint-url "${ENDPOINT}" s3 ls "s3://${BUCKET}/kata-assets/" diff --git a/internal/ateompath/ateompath.go b/internal/ateompath/ateompath.go index 304d53f5f..60142a065 100644 --- a/internal/ateompath/ateompath.go +++ b/internal/ateompath/ateompath.go @@ -34,6 +34,12 @@ func RunSCBinaryPath(sha256 string) string { return filepath.Join(StaticFilesDir, "runsc-"+sha256) } +// RuntimeAssetPath is the content-addressed local path for a fetched non-gVisor +// runtime asset (named so multiple asset kinds don't collide). +func RuntimeAssetPath(name, sha256 string) string { + return filepath.Join(StaticFilesDir, "asset-"+name+"-"+sha256) +} + func AteomPath(podUID string) string { return filepath.Join( BasePath, diff --git a/internal/controllers/workerpool_controller.go b/internal/controllers/workerpool_controller.go index e72d2befd..18e5c3be6 100644 --- a/internal/controllers/workerpool_controller.go +++ b/internal/controllers/workerpool_controller.go @@ -120,6 +120,78 @@ func (r *WorkerPoolReconciler) syncStatus(ctx context.Context, wp *atev1alpha1.W // Deployment managed by a WorkerPool. Only fields owned by this controller // are declared here. func buildDeploymentApplyConfig(wp *atev1alpha1.WorkerPool) *appsv1ac.DeploymentApplyConfiguration { + container := corev1ac.Container(). + WithName("ateom"). + WithImage(wp.Spec.AteomImage). + WithArgs( + "--pod-uid=$(POD_UID)", + ). + WithSecurityContext(corev1ac.SecurityContext(). + WithPrivileged(true). + WithRunAsUser(0). + WithRunAsGroup(0)). + WithEnv( + corev1ac.EnvVar(). + WithName("POD_UID"). + WithValueFrom(corev1ac.EnvVarSource(). + WithFieldRef(corev1ac.ObjectFieldSelector(). + WithFieldPath("metadata.uid"))), + ). + WithVolumeMounts(corev1ac.VolumeMount(). + WithName("run-ateom"). + WithMountPath(ateompath.BasePath)) + + microVM := wp.Spec.Runtime == atev1alpha1.RuntimeMicroVM + + // The micro-VM runtime (kata + cloud-hypervisor) needs /dev/kvm. The + // container is already privileged (so it can also reach vhost devices), but + // we mount /dev/kvm explicitly. Mutate the container before it is added to + // the pod spec (WithContainers copies it by value). + if microVM { + container.WithVolumeMounts(corev1ac.VolumeMount(). + WithName("dev-kvm"). + WithMountPath("/dev/kvm")) + } + + podSpec := corev1ac.PodSpec(). + WithContainers(container). + WithSecurityContext(corev1ac.PodSecurityContext(). + WithRunAsUser(0). + WithRunAsGroup(0)). + WithVolumes(corev1ac.Volume(). + WithName("run-ateom"). + WithHostPath(corev1ac.HostPathVolumeSource(). + WithPath(ateompath.BasePath). + WithType(corev1.HostPathDirectoryOrCreate))) + + // Micro-VM pods must land on KVM-capable, nested-virt nodes: add the + // /dev/kvm host device and pin placement via nodeSelector + toleration on + // ate.dev/runtime=microvm. + // + // TODO: make node selection (and tolerations) configurable on the + // WorkerPool spec instead of hardcoding this convention. GKE attaches NO + // label or taint to nested-virt node pools (verified empirically — unlike + // GKE Sandbox's automatic sandbox.gke.io/runtime=gvisor label + taint), so + // there is no upstream label to select on: ate.dev/runtime=microvm is our + // own convention, applied by hack/create-kind-cluster.sh on kind and via + // --node-labels at GKE node pool creation. The toleration is likewise our + // convention so operators MAY taint dedicated KVM pools to reserve them + // for micro-VM workers; untainted pools schedule fine too. + if microVM { + podSpec. + WithVolumes(corev1ac.Volume(). + WithName("dev-kvm"). + WithHostPath(corev1ac.HostPathVolumeSource(). + WithPath("/dev/kvm"). + WithType(corev1.HostPathCharDev))). + WithNodeSelector(map[string]string{"ate.dev/runtime": string(atev1alpha1.RuntimeMicroVM)}). + WithTolerations(corev1ac.Toleration(). + WithKey("ate.dev/runtime"). + WithOperator(corev1.TolerationOpEqual). + WithValue(string(atev1alpha1.RuntimeMicroVM)). + WithEffect(corev1.TaintEffectNoSchedule)) + } + return appsv1ac.Deployment(deploymentName(wp.Name), wp.Namespace). WithOwnerReferences(metav1ac.OwnerReference(). WithAPIVersion(atev1alpha1.GroupVersion.String()). @@ -136,35 +208,7 @@ func buildDeploymentApplyConfig(wp *atev1alpha1.WorkerPool) *appsv1ac.Deployment WithLabels(map[string]string{ "ate.dev/worker-pool": wp.Name, }). - WithSpec(corev1ac.PodSpec(). - WithContainers(corev1ac.Container(). - WithName("ateom"). - WithImage(wp.Spec.AteomImage). - WithArgs( - "--pod-uid=$(POD_UID)", - ). - WithSecurityContext(corev1ac.SecurityContext(). - WithPrivileged(true). - WithRunAsUser(0). - WithRunAsGroup(0)). - WithEnv( - corev1ac.EnvVar(). - WithName("POD_UID"). - WithValueFrom(corev1ac.EnvVarSource(). - WithFieldRef(corev1ac.ObjectFieldSelector(). - WithFieldPath("metadata.uid"))), - ). - WithVolumeMounts(corev1ac.VolumeMount(). - WithName("run-ateom"). - WithMountPath(ateompath.BasePath))). - WithSecurityContext(corev1ac.PodSecurityContext(). - WithRunAsUser(0). - WithRunAsGroup(0)). - WithVolumes(corev1ac.Volume(). - WithName("run-ateom"). - WithHostPath(corev1ac.HostPathVolumeSource(). - WithPath(ateompath.BasePath). - WithType(corev1.HostPathDirectoryOrCreate)))))) + WithSpec(podSpec))) } // SetupWithManager sets up the controller with the Manager. diff --git a/internal/controllers/workerpool_podshape_test.go b/internal/controllers/workerpool_podshape_test.go new file mode 100644 index 000000000..5019218cf --- /dev/null +++ b/internal/controllers/workerpool_podshape_test.go @@ -0,0 +1,99 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controllers + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + + atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1" +) + +// TestBuildDeploymentPodShape asserts the micro-VM runtime adds the +// /dev/kvm device, nodeSelector, and toleration; gvisor (default) does not. +func TestBuildDeploymentPodShape(t *testing.T) { + tests := []struct { + name string + runtime atev1alpha1.Runtime + wantKVM bool + wantPlaced bool + }{ + {name: "gvisor default", runtime: "", wantKVM: false, wantPlaced: false}, + {name: "gvisor explicit", runtime: atev1alpha1.RuntimeGvisor, wantKVM: false, wantPlaced: false}, + {name: "microvm", runtime: atev1alpha1.RuntimeMicroVM, wantKVM: true, wantPlaced: true}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + wp := &atev1alpha1.WorkerPool{} + wp.Name = "wp" + wp.Namespace = "ns" + wp.Spec.Replicas = 1 + wp.Spec.AteomImage = "example.com/ateom:latest" + wp.Spec.Runtime = tc.runtime + + dep := buildDeploymentApplyConfig(wp) + ps := dep.Spec.Template.Spec + + // /dev/kvm volume + mount. + hasKVMVol := false + for _, v := range ps.Volumes { + if v.Name != nil && *v.Name == "dev-kvm" { + hasKVMVol = true + if v.HostPath == nil || v.HostPath.Path == nil || *v.HostPath.Path != "/dev/kvm" { + t.Errorf("dev-kvm volume hostPath = %+v, want /dev/kvm", v.HostPath) + } + if v.HostPath.Type == nil || *v.HostPath.Type != corev1.HostPathCharDev { + t.Errorf("dev-kvm volume type = %v, want CharDevice", v.HostPath.Type) + } + } + } + hasKVMMount := false + for _, c := range ps.Containers { + for _, m := range c.VolumeMounts { + if m.MountPath != nil && *m.MountPath == "/dev/kvm" { + hasKVMMount = true + } + } + } + if hasKVMVol != tc.wantKVM || hasKVMMount != tc.wantKVM { + t.Errorf("kvm present vol=%v mount=%v, want %v", hasKVMVol, hasKVMMount, tc.wantKVM) + } + + // nodeSelector + toleration on ate.dev/runtime. + _, hasSelector := ps.NodeSelector["ate.dev/runtime"] + hasToleration := false + for _, tol := range ps.Tolerations { + if tol.Key != nil && *tol.Key == "ate.dev/runtime" { + hasToleration = true + } + } + if hasSelector != tc.wantPlaced || hasToleration != tc.wantPlaced { + t.Errorf("placement selector=%v toleration=%v, want %v", hasSelector, hasToleration, tc.wantPlaced) + } + + // The base run-ateom hostPath mount must always be present. + foundBase := false + for _, v := range ps.Volumes { + if v.Name != nil && *v.Name == "run-ateom" { + foundBase = true + } + } + if !foundBase { + t.Error("run-ateom volume missing") + } + }) + } +} diff --git a/internal/proto/ateletpb/atelet.pb.go b/internal/proto/ateletpb/atelet.pb.go index e3a55dd50..3a6d34c02 100644 --- a/internal/proto/ateletpb/atelet.pb.go +++ b/internal/proto/ateletpb/atelet.pb.go @@ -43,6 +43,7 @@ type RunRequest struct { ActorId string `protobuf:"bytes,5,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` Runsc *RunscConfig `protobuf:"bytes,8,opt,name=runsc,proto3" json:"runsc,omitempty"` Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` + RuntimeAssets *RuntimeAssetsConfig `protobuf:"bytes,9,opt,name=runtime_assets,json=runtimeAssets,proto3" json:"runtime_assets,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -119,6 +120,13 @@ func (x *RunRequest) GetSpec() *WorkloadSpec { return nil } +func (x *RunRequest) GetRuntimeAssets() *RuntimeAssetsConfig { + if x != nil { + return x.RuntimeAssets + } + return nil +} + type GCPAuthenticationConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Use bool `protobuf:"varint,1,opt,name=use,proto3" json:"use,omitempty"` @@ -323,6 +331,127 @@ func (x *RunscConfig) GetAuthentication() *AuthenticationConfig { return nil } +// RuntimeAsset is one content-addressed file atelet fetches for a non-gVisor +// runtime (e.g. the kata shim, cloud-hypervisor, virtiofsd, guest kernel/image, +// base configuration.toml). +type RuntimeAsset struct { + state protoimpl.MessageState `protogen:"open.v1"` + // sha256_hash both names the cached file and verifies its integrity. + Sha256Hash string `protobuf:"bytes,1,opt,name=sha256_hash,json=sha256Hash,proto3" json:"sha256_hash,omitempty"` + // gs:// URL to download from (resolved against the authenticated client when + // authentication is set, else the anonymous public client). + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RuntimeAsset) Reset() { + *x = RuntimeAsset{} + mi := &file_atelet_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RuntimeAsset) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RuntimeAsset) ProtoMessage() {} + +func (x *RuntimeAsset) ProtoReflect() protoreflect.Message { + mi := &file_atelet_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RuntimeAsset.ProtoReflect.Descriptor instead. +func (*RuntimeAsset) Descriptor() ([]byte, []int) { + return file_atelet_proto_rawDescGZIP(), []int{5} +} + +func (x *RuntimeAsset) GetSha256Hash() string { + if x != nil { + return x.Sha256Hash + } + return "" +} + +func (x *RuntimeAsset) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +// RuntimeAssetsConfig describes the asset set for a non-gVisor runtime. atelet +// fetches each asset content-addressed (like runsc) and passes the local paths +// to ateom. Empty/runtime="gvisor" means use the runsc path instead. +type RuntimeAssetsConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Runtime string `protobuf:"bytes,1,opt,name=runtime,proto3" json:"runtime,omitempty"` // "gvisor" | "microvm" + Assets map[string]*RuntimeAsset `protobuf:"bytes,2,rep,name=assets,proto3" json:"assets,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // name -> asset + Authentication *AuthenticationConfig `protobuf:"bytes,3,opt,name=authentication,proto3" json:"authentication,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RuntimeAssetsConfig) Reset() { + *x = RuntimeAssetsConfig{} + mi := &file_atelet_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RuntimeAssetsConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RuntimeAssetsConfig) ProtoMessage() {} + +func (x *RuntimeAssetsConfig) ProtoReflect() protoreflect.Message { + mi := &file_atelet_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RuntimeAssetsConfig.ProtoReflect.Descriptor instead. +func (*RuntimeAssetsConfig) Descriptor() ([]byte, []int) { + return file_atelet_proto_rawDescGZIP(), []int{6} +} + +func (x *RuntimeAssetsConfig) GetRuntime() string { + if x != nil { + return x.Runtime + } + return "" +} + +func (x *RuntimeAssetsConfig) GetAssets() map[string]*RuntimeAsset { + if x != nil { + return x.Assets + } + return nil +} + +func (x *RuntimeAssetsConfig) GetAuthentication() *AuthenticationConfig { + if x != nil { + return x.Authentication + } + return nil +} + // WorkloadSpec parallels Pod, but with far fewer configurable fields. type WorkloadSpec struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -334,7 +463,7 @@ type WorkloadSpec struct { func (x *WorkloadSpec) Reset() { *x = WorkloadSpec{} - mi := &file_atelet_proto_msgTypes[5] + mi := &file_atelet_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -346,7 +475,7 @@ func (x *WorkloadSpec) String() string { func (*WorkloadSpec) ProtoMessage() {} func (x *WorkloadSpec) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[5] + mi := &file_atelet_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -359,7 +488,7 @@ func (x *WorkloadSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkloadSpec.ProtoReflect.Descriptor instead. func (*WorkloadSpec) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{5} + return file_atelet_proto_rawDescGZIP(), []int{7} } func (x *WorkloadSpec) GetContainers() []*Container { @@ -388,7 +517,7 @@ type Container struct { func (x *Container) Reset() { *x = Container{} - mi := &file_atelet_proto_msgTypes[6] + mi := &file_atelet_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -400,7 +529,7 @@ func (x *Container) String() string { func (*Container) ProtoMessage() {} func (x *Container) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[6] + mi := &file_atelet_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -413,7 +542,7 @@ func (x *Container) ProtoReflect() protoreflect.Message { // Deprecated: Use Container.ProtoReflect.Descriptor instead. func (*Container) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{6} + return file_atelet_proto_rawDescGZIP(), []int{8} } func (x *Container) GetName() string { @@ -454,7 +583,7 @@ type EnvEntry struct { func (x *EnvEntry) Reset() { *x = EnvEntry{} - mi := &file_atelet_proto_msgTypes[7] + mi := &file_atelet_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -466,7 +595,7 @@ func (x *EnvEntry) String() string { func (*EnvEntry) ProtoMessage() {} func (x *EnvEntry) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[7] + mi := &file_atelet_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -479,7 +608,7 @@ func (x *EnvEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use EnvEntry.ProtoReflect.Descriptor instead. func (*EnvEntry) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{7} + return file_atelet_proto_rawDescGZIP(), []int{9} } func (x *EnvEntry) GetName() string { @@ -504,7 +633,7 @@ type RunResponse struct { func (x *RunResponse) Reset() { *x = RunResponse{} - mi := &file_atelet_proto_msgTypes[8] + mi := &file_atelet_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -516,7 +645,7 @@ func (x *RunResponse) String() string { func (*RunResponse) ProtoMessage() {} func (x *RunResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[8] + mi := &file_atelet_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -529,7 +658,7 @@ func (x *RunResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunResponse.ProtoReflect.Descriptor instead. func (*RunResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{8} + return file_atelet_proto_rawDescGZIP(), []int{10} } type CheckpointRequest struct { @@ -550,14 +679,15 @@ type CheckpointRequest struct { // can restore a checkpoint. // // For example: "gs://bucket/actors/1234/snapshots/5678/" - SnapshotUriPrefix string `protobuf:"bytes,8,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` + SnapshotUriPrefix string `protobuf:"bytes,8,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` + RuntimeAssets *RuntimeAssetsConfig `protobuf:"bytes,9,opt,name=runtime_assets,json=runtimeAssets,proto3" json:"runtime_assets,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CheckpointRequest) Reset() { *x = CheckpointRequest{} - mi := &file_atelet_proto_msgTypes[9] + mi := &file_atelet_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -569,7 +699,7 @@ func (x *CheckpointRequest) String() string { func (*CheckpointRequest) ProtoMessage() {} func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[9] + mi := &file_atelet_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -582,7 +712,7 @@ func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointRequest.ProtoReflect.Descriptor instead. func (*CheckpointRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{9} + return file_atelet_proto_rawDescGZIP(), []int{11} } func (x *CheckpointRequest) GetTargetAteomUid() string { @@ -634,6 +764,13 @@ func (x *CheckpointRequest) GetSnapshotUriPrefix() string { return "" } +func (x *CheckpointRequest) GetRuntimeAssets() *RuntimeAssetsConfig { + if x != nil { + return x.RuntimeAssets + } + return nil +} + type CheckpointResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -642,7 +779,7 @@ type CheckpointResponse struct { func (x *CheckpointResponse) Reset() { *x = CheckpointResponse{} - mi := &file_atelet_proto_msgTypes[10] + mi := &file_atelet_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -654,7 +791,7 @@ func (x *CheckpointResponse) String() string { func (*CheckpointResponse) ProtoMessage() {} func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[10] + mi := &file_atelet_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -667,7 +804,7 @@ func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointResponse.ProtoReflect.Descriptor instead. func (*CheckpointResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{10} + return file_atelet_proto_rawDescGZIP(), []int{12} } type RestoreRequest struct { @@ -679,14 +816,15 @@ type RestoreRequest struct { Runsc *RunscConfig `protobuf:"bytes,6,opt,name=runsc,proto3" json:"runsc,omitempty"` Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` // The object storage URI prefix of the snapshot to restore. - SnapshotUriPrefix string `protobuf:"bytes,8,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` + SnapshotUriPrefix string `protobuf:"bytes,8,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` + RuntimeAssets *RuntimeAssetsConfig `protobuf:"bytes,9,opt,name=runtime_assets,json=runtimeAssets,proto3" json:"runtime_assets,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RestoreRequest) Reset() { *x = RestoreRequest{} - mi := &file_atelet_proto_msgTypes[11] + mi := &file_atelet_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -698,7 +836,7 @@ func (x *RestoreRequest) String() string { func (*RestoreRequest) ProtoMessage() {} func (x *RestoreRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[11] + mi := &file_atelet_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -711,7 +849,7 @@ func (x *RestoreRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreRequest.ProtoReflect.Descriptor instead. func (*RestoreRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{11} + return file_atelet_proto_rawDescGZIP(), []int{13} } func (x *RestoreRequest) GetTargetAteomUid() string { @@ -763,6 +901,13 @@ func (x *RestoreRequest) GetSnapshotUriPrefix() string { return "" } +func (x *RestoreRequest) GetRuntimeAssets() *RuntimeAssetsConfig { + if x != nil { + return x.RuntimeAssets + } + return nil +} + type RestoreResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -771,7 +916,7 @@ type RestoreResponse struct { func (x *RestoreResponse) Reset() { *x = RestoreResponse{} - mi := &file_atelet_proto_msgTypes[12] + mi := &file_atelet_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -783,7 +928,7 @@ func (x *RestoreResponse) String() string { func (*RestoreResponse) ProtoMessage() {} func (x *RestoreResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[12] + mi := &file_atelet_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -796,14 +941,14 @@ func (x *RestoreResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreResponse.ProtoReflect.Descriptor instead. func (*RestoreResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{12} + return file_atelet_proto_rawDescGZIP(), []int{14} } var File_atelet_proto protoreflect.FileDescriptor const file_atelet_proto_rawDesc = "" + "\n" + - "\fatelet.proto\x12\x06atelet\"\x90\x02\n" + + "\fatelet.proto\x12\x06atelet\"\xd4\x02\n" + "\n" + "RunRequest\x12(\n" + "\x10target_ateom_uid\x18\x01 \x01(\tR\x0etargetAteomUid\x128\n" + @@ -811,7 +956,8 @@ const file_atelet_proto_rawDesc = "" + "\x13actor_template_name\x18\x04 \x01(\tR\x11actorTemplateName\x12\x19\n" + "\bactor_id\x18\x05 \x01(\tR\aactorId\x12)\n" + "\x05runsc\x18\b \x01(\v2\x13.atelet.RunscConfigR\x05runsc\x12(\n" + - "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\"+\n" + + "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\x12B\n" + + "\x0eruntime_assets\x18\t \x01(\v2\x1b.atelet.RuntimeAssetsConfigR\rruntimeAssets\"+\n" + "\x17GCPAuthenticationConfig\x12\x10\n" + "\x03use\x18\x01 \x01(\bR\x03use\"I\n" + "\x14AuthenticationConfig\x121\n" + @@ -823,7 +969,18 @@ const file_atelet_proto_rawDesc = "" + "\vRunscConfig\x121\n" + "\x05amd64\x18\x01 \x01(\v2\x1b.atelet.RunscPlatformConfigR\x05amd64\x121\n" + "\x05arm64\x18\x02 \x01(\v2\x1b.atelet.RunscPlatformConfigR\x05arm64\x12D\n" + - "\x0eauthentication\x18\x03 \x01(\v2\x1c.atelet.AuthenticationConfigR\x0eauthentication\"b\n" + + "\x0eauthentication\x18\x03 \x01(\v2\x1c.atelet.AuthenticationConfigR\x0eauthentication\"A\n" + + "\fRuntimeAsset\x12\x1f\n" + + "\vsha256_hash\x18\x01 \x01(\tR\n" + + "sha256Hash\x12\x10\n" + + "\x03url\x18\x02 \x01(\tR\x03url\"\x87\x02\n" + + "\x13RuntimeAssetsConfig\x12\x18\n" + + "\aruntime\x18\x01 \x01(\tR\aruntime\x12?\n" + + "\x06assets\x18\x02 \x03(\v2'.atelet.RuntimeAssetsConfig.AssetsEntryR\x06assets\x12D\n" + + "\x0eauthentication\x18\x03 \x01(\v2\x1c.atelet.AuthenticationConfigR\x0eauthentication\x1aO\n" + + "\vAssetsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + + "\x05value\x18\x02 \x01(\v2\x14.atelet.RuntimeAssetR\x05value:\x028\x01\"b\n" + "\fWorkloadSpec\x121\n" + "\n" + "containers\x18\x01 \x03(\v2\x11.atelet.ContainerR\n" + @@ -838,7 +995,7 @@ const file_atelet_proto_rawDesc = "" + "\bEnvEntry\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value\"\r\n" + - "\vRunResponse\"\xc7\x02\n" + + "\vRunResponse\"\x8b\x03\n" + "\x11CheckpointRequest\x12(\n" + "\x10target_ateom_uid\x18\x01 \x01(\tR\x0etargetAteomUid\x128\n" + "\x18actor_template_namespace\x18\x03 \x01(\tR\x16actorTemplateNamespace\x12.\n" + @@ -846,8 +1003,9 @@ const file_atelet_proto_rawDesc = "" + "\bactor_id\x18\x05 \x01(\tR\aactorId\x12)\n" + "\x05runsc\x18\x06 \x01(\v2\x13.atelet.RunscConfigR\x05runsc\x12(\n" + "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\x12.\n" + - "\x13snapshot_uri_prefix\x18\b \x01(\tR\x11snapshotUriPrefix\"\x14\n" + - "\x12CheckpointResponse\"\xc4\x02\n" + + "\x13snapshot_uri_prefix\x18\b \x01(\tR\x11snapshotUriPrefix\x12B\n" + + "\x0eruntime_assets\x18\t \x01(\v2\x1b.atelet.RuntimeAssetsConfigR\rruntimeAssets\"\x14\n" + + "\x12CheckpointResponse\"\x88\x03\n" + "\x0eRestoreRequest\x12(\n" + "\x10target_ateom_uid\x18\x01 \x01(\tR\x0etargetAteomUid\x128\n" + "\x18actor_template_namespace\x18\x03 \x01(\tR\x16actorTemplateNamespace\x12.\n" + @@ -855,7 +1013,8 @@ const file_atelet_proto_rawDesc = "" + "\bactor_id\x18\x05 \x01(\tR\aactorId\x12)\n" + "\x05runsc\x18\x06 \x01(\v2\x13.atelet.RunscConfigR\x05runsc\x12(\n" + "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\x12.\n" + - "\x13snapshot_uri_prefix\x18\b \x01(\tR\x11snapshotUriPrefix\"\x11\n" + + "\x13snapshot_uri_prefix\x18\b \x01(\tR\x11snapshotUriPrefix\x12B\n" + + "\x0eruntime_assets\x18\t \x01(\v2\x1b.atelet.RuntimeAssetsConfigR\rruntimeAssets\"\x11\n" + "\x0fRestoreResponse2\xc4\x01\n" + "\vAteomHerder\x120\n" + "\x03Run\x12\x12.atelet.RunRequest\x1a\x13.atelet.RunResponse\"\x00\x12E\n" + @@ -875,46 +1034,55 @@ func file_atelet_proto_rawDescGZIP() []byte { return file_atelet_proto_rawDescData } -var file_atelet_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_atelet_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_atelet_proto_goTypes = []any{ (*RunRequest)(nil), // 0: atelet.RunRequest (*GCPAuthenticationConfig)(nil), // 1: atelet.GCPAuthenticationConfig (*AuthenticationConfig)(nil), // 2: atelet.AuthenticationConfig (*RunscPlatformConfig)(nil), // 3: atelet.RunscPlatformConfig (*RunscConfig)(nil), // 4: atelet.RunscConfig - (*WorkloadSpec)(nil), // 5: atelet.WorkloadSpec - (*Container)(nil), // 6: atelet.Container - (*EnvEntry)(nil), // 7: atelet.EnvEntry - (*RunResponse)(nil), // 8: atelet.RunResponse - (*CheckpointRequest)(nil), // 9: atelet.CheckpointRequest - (*CheckpointResponse)(nil), // 10: atelet.CheckpointResponse - (*RestoreRequest)(nil), // 11: atelet.RestoreRequest - (*RestoreResponse)(nil), // 12: atelet.RestoreResponse + (*RuntimeAsset)(nil), // 5: atelet.RuntimeAsset + (*RuntimeAssetsConfig)(nil), // 6: atelet.RuntimeAssetsConfig + (*WorkloadSpec)(nil), // 7: atelet.WorkloadSpec + (*Container)(nil), // 8: atelet.Container + (*EnvEntry)(nil), // 9: atelet.EnvEntry + (*RunResponse)(nil), // 10: atelet.RunResponse + (*CheckpointRequest)(nil), // 11: atelet.CheckpointRequest + (*CheckpointResponse)(nil), // 12: atelet.CheckpointResponse + (*RestoreRequest)(nil), // 13: atelet.RestoreRequest + (*RestoreResponse)(nil), // 14: atelet.RestoreResponse + nil, // 15: atelet.RuntimeAssetsConfig.AssetsEntry } var file_atelet_proto_depIdxs = []int32{ 4, // 0: atelet.RunRequest.runsc:type_name -> atelet.RunscConfig - 5, // 1: atelet.RunRequest.spec:type_name -> atelet.WorkloadSpec - 1, // 2: atelet.AuthenticationConfig.gcp:type_name -> atelet.GCPAuthenticationConfig - 3, // 3: atelet.RunscConfig.amd64:type_name -> atelet.RunscPlatformConfig - 3, // 4: atelet.RunscConfig.arm64:type_name -> atelet.RunscPlatformConfig - 2, // 5: atelet.RunscConfig.authentication:type_name -> atelet.AuthenticationConfig - 6, // 6: atelet.WorkloadSpec.containers:type_name -> atelet.Container - 7, // 7: atelet.Container.env:type_name -> atelet.EnvEntry - 4, // 8: atelet.CheckpointRequest.runsc:type_name -> atelet.RunscConfig - 5, // 9: atelet.CheckpointRequest.spec:type_name -> atelet.WorkloadSpec - 4, // 10: atelet.RestoreRequest.runsc:type_name -> atelet.RunscConfig - 5, // 11: atelet.RestoreRequest.spec:type_name -> atelet.WorkloadSpec - 0, // 12: atelet.AteomHerder.Run:input_type -> atelet.RunRequest - 9, // 13: atelet.AteomHerder.Checkpoint:input_type -> atelet.CheckpointRequest - 11, // 14: atelet.AteomHerder.Restore:input_type -> atelet.RestoreRequest - 8, // 15: atelet.AteomHerder.Run:output_type -> atelet.RunResponse - 10, // 16: atelet.AteomHerder.Checkpoint:output_type -> atelet.CheckpointResponse - 12, // 17: atelet.AteomHerder.Restore:output_type -> atelet.RestoreResponse - 15, // [15:18] is the sub-list for method output_type - 12, // [12:15] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 7, // 1: atelet.RunRequest.spec:type_name -> atelet.WorkloadSpec + 6, // 2: atelet.RunRequest.runtime_assets:type_name -> atelet.RuntimeAssetsConfig + 1, // 3: atelet.AuthenticationConfig.gcp:type_name -> atelet.GCPAuthenticationConfig + 3, // 4: atelet.RunscConfig.amd64:type_name -> atelet.RunscPlatformConfig + 3, // 5: atelet.RunscConfig.arm64:type_name -> atelet.RunscPlatformConfig + 2, // 6: atelet.RunscConfig.authentication:type_name -> atelet.AuthenticationConfig + 15, // 7: atelet.RuntimeAssetsConfig.assets:type_name -> atelet.RuntimeAssetsConfig.AssetsEntry + 2, // 8: atelet.RuntimeAssetsConfig.authentication:type_name -> atelet.AuthenticationConfig + 8, // 9: atelet.WorkloadSpec.containers:type_name -> atelet.Container + 9, // 10: atelet.Container.env:type_name -> atelet.EnvEntry + 4, // 11: atelet.CheckpointRequest.runsc:type_name -> atelet.RunscConfig + 7, // 12: atelet.CheckpointRequest.spec:type_name -> atelet.WorkloadSpec + 6, // 13: atelet.CheckpointRequest.runtime_assets:type_name -> atelet.RuntimeAssetsConfig + 4, // 14: atelet.RestoreRequest.runsc:type_name -> atelet.RunscConfig + 7, // 15: atelet.RestoreRequest.spec:type_name -> atelet.WorkloadSpec + 6, // 16: atelet.RestoreRequest.runtime_assets:type_name -> atelet.RuntimeAssetsConfig + 5, // 17: atelet.RuntimeAssetsConfig.AssetsEntry.value:type_name -> atelet.RuntimeAsset + 0, // 18: atelet.AteomHerder.Run:input_type -> atelet.RunRequest + 11, // 19: atelet.AteomHerder.Checkpoint:input_type -> atelet.CheckpointRequest + 13, // 20: atelet.AteomHerder.Restore:input_type -> atelet.RestoreRequest + 10, // 21: atelet.AteomHerder.Run:output_type -> atelet.RunResponse + 12, // 22: atelet.AteomHerder.Checkpoint:output_type -> atelet.CheckpointResponse + 14, // 23: atelet.AteomHerder.Restore:output_type -> atelet.RestoreResponse + 21, // [21:24] is the sub-list for method output_type + 18, // [18:21] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_atelet_proto_init() } @@ -928,7 +1096,7 @@ func file_atelet_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_atelet_proto_rawDesc), len(file_atelet_proto_rawDesc)), NumEnums: 0, - NumMessages: 13, + NumMessages: 16, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/proto/ateletpb/atelet.proto b/internal/proto/ateletpb/atelet.proto index 792cffc98..016bd1624 100644 --- a/internal/proto/ateletpb/atelet.proto +++ b/internal/proto/ateletpb/atelet.proto @@ -42,6 +42,8 @@ message RunRequest { RunscConfig runsc = 8; WorkloadSpec spec = 7; + RuntimeAssetsConfig runtime_assets = 9; + } message GCPAuthenticationConfig { @@ -69,6 +71,26 @@ message RunscConfig { AuthenticationConfig authentication = 3; } +// RuntimeAsset is one content-addressed file atelet fetches for a non-gVisor +// runtime (e.g. the kata shim, cloud-hypervisor, virtiofsd, guest kernel/image, +// base configuration.toml). +message RuntimeAsset { + // sha256_hash both names the cached file and verifies its integrity. + string sha256_hash = 1; + // gs:// URL to download from (resolved against the authenticated client when + // authentication is set, else the anonymous public client). + string url = 2; +} + +// RuntimeAssetsConfig describes the asset set for a non-gVisor runtime. atelet +// fetches each asset content-addressed (like runsc) and passes the local paths +// to ateom. Empty/runtime="gvisor" means use the runsc path instead. +message RuntimeAssetsConfig { + string runtime = 1; // "gvisor" | "microvm" + map assets = 2; // name -> asset + AuthenticationConfig authentication = 3; +} + // WorkloadSpec parallels Pod, but with far fewer configurable fields. message WorkloadSpec { repeated Container containers = 1; @@ -112,6 +134,8 @@ message CheckpointRequest { // // For example: "gs://bucket/actors/1234/snapshots/5678/" string snapshot_uri_prefix = 8; + RuntimeAssetsConfig runtime_assets = 9; + } message CheckpointResponse { @@ -131,6 +155,8 @@ message RestoreRequest { // The object storage URI prefix of the snapshot to restore. string snapshot_uri_prefix = 8; + RuntimeAssetsConfig runtime_assets = 9; + } message RestoreResponse { diff --git a/internal/proto/ateompb/ateom.pb.go b/internal/proto/ateompb/ateom.pb.go index 9114a309b..d5040b7fb 100644 --- a/internal/proto/ateompb/ateom.pb.go +++ b/internal/proto/ateompb/ateom.pb.go @@ -42,8 +42,13 @@ type RunWorkloadRequest struct { ActorId string `protobuf:"bytes,3,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` RunscPath string `protobuf:"bytes,4,opt,name=runsc_path,json=runscPath,proto3" json:"runsc_path,omitempty"` Spec *WorkloadSpec `protobuf:"bytes,5,opt,name=spec,proto3" json:"spec,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // runtime_asset_paths maps a runtime asset name (e.g. "kata-shim", + // "cloud-hypervisor", "virtiofsd", "kata-kernel", "kata-image", "kata-config") + // to the local on-disk path atelet fetched it to (content-addressed, like + // runsc_path). Empty for the gVisor runtime, which uses runsc_path. + RuntimeAssetPaths map[string]string `protobuf:"bytes,7,rep,name=runtime_asset_paths,json=runtimeAssetPaths,proto3" json:"runtime_asset_paths,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RunWorkloadRequest) Reset() { @@ -111,6 +116,13 @@ func (x *RunWorkloadRequest) GetSpec() *WorkloadSpec { return nil } +func (x *RunWorkloadRequest) GetRuntimeAssetPaths() map[string]string { + if x != nil { + return x.RuntimeAssetPaths + } + return nil +} + // WorkloadSpec parallels Pod, but with far fewer configurable fields. type WorkloadSpec struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -252,6 +264,11 @@ type CheckpointWorkloadRequest struct { // // For example: "gs://bucket/actors/1234/snapshots/5678/" SnapshotUriPrefix string `protobuf:"bytes,6,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` + // runtime_asset_paths maps a runtime asset name (e.g. "kata-shim", + // "cloud-hypervisor", "virtiofsd", "kata-kernel", "kata-image", "kata-config") + // to the local on-disk path atelet fetched it to (content-addressed, like + // runsc_path). Empty for the gVisor runtime, which uses runsc_path. + RuntimeAssetPaths map[string]string `protobuf:"bytes,7,rep,name=runtime_asset_paths,json=runtimeAssetPaths,proto3" json:"runtime_asset_paths,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -328,8 +345,19 @@ func (x *CheckpointWorkloadRequest) GetSnapshotUriPrefix() string { return "" } +func (x *CheckpointWorkloadRequest) GetRuntimeAssetPaths() map[string]string { + if x != nil { + return x.RuntimeAssetPaths + } + return nil +} + type CheckpointWorkloadResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` + state protoimpl.MessageState `protogen:"open.v1"` + // snapshot_files lists the files ateom wrote into the checkpoint directory + // (relative names) for atelet to ship to object storage. Empty for runtimes + // (e.g. gVisor) that use atelet's legacy fixed file set. + SnapshotFiles []string `protobuf:"bytes,1,rep,name=snapshot_files,json=snapshotFiles,proto3" json:"snapshot_files,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -364,6 +392,13 @@ func (*CheckpointWorkloadResponse) Descriptor() ([]byte, []int) { return file_ateom_proto_rawDescGZIP(), []int{5} } +func (x *CheckpointWorkloadResponse) GetSnapshotFiles() []string { + if x != nil { + return x.SnapshotFiles + } + return nil +} + type RestoreWorkloadRequest struct { state protoimpl.MessageState `protogen:"open.v1"` ActorTemplateNamespace string `protobuf:"bytes,1,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"` @@ -373,6 +408,11 @@ type RestoreWorkloadRequest struct { Spec *WorkloadSpec `protobuf:"bytes,5,opt,name=spec,proto3" json:"spec,omitempty"` // The object storage URI prefix of the snapshot to restore. SnapshotUriPrefix string `protobuf:"bytes,6,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` + // runtime_asset_paths maps a runtime asset name (e.g. "kata-shim", + // "cloud-hypervisor", "virtiofsd", "kata-kernel", "kata-image", "kata-config") + // to the local on-disk path atelet fetched it to (content-addressed, like + // runsc_path). Empty for the gVisor runtime, which uses runsc_path. + RuntimeAssetPaths map[string]string `protobuf:"bytes,7,rep,name=runtime_asset_paths,json=runtimeAssetPaths,proto3" json:"runtime_asset_paths,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -449,6 +489,13 @@ func (x *RestoreWorkloadRequest) GetSnapshotUriPrefix() string { return "" } +func (x *RestoreWorkloadRequest) GetRuntimeAssetPaths() map[string]string { + if x != nil { + return x.RuntimeAssetPaths + } + return nil +} + type RestoreWorkloadResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -489,21 +536,25 @@ var File_ateom_proto protoreflect.FileDescriptor const file_ateom_proto_rawDesc = "" + "\n" + - "\vateom.proto\x12\x05ateom\"\xe1\x01\n" + + "\vateom.proto\x12\x05ateom\"\x89\x03\n" + "\x12RunWorkloadRequest\x128\n" + "\x18actor_template_namespace\x18\x01 \x01(\tR\x16actorTemplateNamespace\x12.\n" + "\x13actor_template_name\x18\x02 \x01(\tR\x11actorTemplateName\x12\x19\n" + "\bactor_id\x18\x03 \x01(\tR\aactorId\x12\x1d\n" + "\n" + "runsc_path\x18\x04 \x01(\tR\trunscPath\x12'\n" + - "\x04spec\x18\x05 \x01(\v2\x13.ateom.WorkloadSpecR\x04spec\"@\n" + + "\x04spec\x18\x05 \x01(\v2\x13.ateom.WorkloadSpecR\x04spec\x12`\n" + + "\x13runtime_asset_paths\x18\a \x03(\v20.ateom.RunWorkloadRequest.RuntimeAssetPathsEntryR\x11runtimeAssetPaths\x1aD\n" + + "\x16RuntimeAssetPathsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"@\n" + "\fWorkloadSpec\x120\n" + "\n" + "containers\x18\x01 \x03(\v2\x10.ateom.ContainerR\n" + "containers\"\x1f\n" + "\tContainer\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\"\x15\n" + - "\x13RunWorkloadResponse\"\x98\x02\n" + + "\x13RunWorkloadResponse\"\xc7\x03\n" + "\x19CheckpointWorkloadRequest\x128\n" + "\x18actor_template_namespace\x18\x01 \x01(\tR\x16actorTemplateNamespace\x12.\n" + "\x13actor_template_name\x18\x02 \x01(\tR\x11actorTemplateName\x12\x19\n" + @@ -511,8 +562,13 @@ const file_ateom_proto_rawDesc = "" + "\n" + "runsc_path\x18\x04 \x01(\tR\trunscPath\x12'\n" + "\x04spec\x18\x05 \x01(\v2\x13.ateom.WorkloadSpecR\x04spec\x12.\n" + - "\x13snapshot_uri_prefix\x18\x06 \x01(\tR\x11snapshotUriPrefix\"\x1c\n" + - "\x1aCheckpointWorkloadResponse\"\x95\x02\n" + + "\x13snapshot_uri_prefix\x18\x06 \x01(\tR\x11snapshotUriPrefix\x12g\n" + + "\x13runtime_asset_paths\x18\a \x03(\v27.ateom.CheckpointWorkloadRequest.RuntimeAssetPathsEntryR\x11runtimeAssetPaths\x1aD\n" + + "\x16RuntimeAssetPathsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"C\n" + + "\x1aCheckpointWorkloadResponse\x12%\n" + + "\x0esnapshot_files\x18\x01 \x03(\tR\rsnapshotFiles\"\xc1\x03\n" + "\x16RestoreWorkloadRequest\x128\n" + "\x18actor_template_namespace\x18\x01 \x01(\tR\x16actorTemplateNamespace\x12.\n" + "\x13actor_template_name\x18\x02 \x01(\tR\x11actorTemplateName\x12\x19\n" + @@ -520,7 +576,11 @@ const file_ateom_proto_rawDesc = "" + "\n" + "runsc_path\x18\x04 \x01(\tR\trunscPath\x12'\n" + "\x04spec\x18\x05 \x01(\v2\x13.ateom.WorkloadSpecR\x04spec\x12.\n" + - "\x13snapshot_uri_prefix\x18\x06 \x01(\tR\x11snapshotUriPrefix\"\x19\n" + + "\x13snapshot_uri_prefix\x18\x06 \x01(\tR\x11snapshotUriPrefix\x12d\n" + + "\x13runtime_asset_paths\x18\a \x03(\v24.ateom.RestoreWorkloadRequest.RuntimeAssetPathsEntryR\x11runtimeAssetPaths\x1aD\n" + + "\x16RuntimeAssetPathsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x19\n" + "\x17RestoreWorkloadResponse2\x80\x02\n" + "\x05Ateom\x12F\n" + "\vRunWorkload\x12\x19.ateom.RunWorkloadRequest\x1a\x1a.ateom.RunWorkloadResponse\"\x00\x12[\n" + @@ -539,7 +599,7 @@ func file_ateom_proto_rawDescGZIP() []byte { return file_ateom_proto_rawDescData } -var file_ateom_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_ateom_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_ateom_proto_goTypes = []any{ (*RunWorkloadRequest)(nil), // 0: ateom.RunWorkloadRequest (*WorkloadSpec)(nil), // 1: ateom.WorkloadSpec @@ -549,23 +609,29 @@ var file_ateom_proto_goTypes = []any{ (*CheckpointWorkloadResponse)(nil), // 5: ateom.CheckpointWorkloadResponse (*RestoreWorkloadRequest)(nil), // 6: ateom.RestoreWorkloadRequest (*RestoreWorkloadResponse)(nil), // 7: ateom.RestoreWorkloadResponse + nil, // 8: ateom.RunWorkloadRequest.RuntimeAssetPathsEntry + nil, // 9: ateom.CheckpointWorkloadRequest.RuntimeAssetPathsEntry + nil, // 10: ateom.RestoreWorkloadRequest.RuntimeAssetPathsEntry } var file_ateom_proto_depIdxs = []int32{ - 1, // 0: ateom.RunWorkloadRequest.spec:type_name -> ateom.WorkloadSpec - 2, // 1: ateom.WorkloadSpec.containers:type_name -> ateom.Container - 1, // 2: ateom.CheckpointWorkloadRequest.spec:type_name -> ateom.WorkloadSpec - 1, // 3: ateom.RestoreWorkloadRequest.spec:type_name -> ateom.WorkloadSpec - 0, // 4: ateom.Ateom.RunWorkload:input_type -> ateom.RunWorkloadRequest - 4, // 5: ateom.Ateom.CheckpointWorkload:input_type -> ateom.CheckpointWorkloadRequest - 6, // 6: ateom.Ateom.RestoreWorkload:input_type -> ateom.RestoreWorkloadRequest - 3, // 7: ateom.Ateom.RunWorkload:output_type -> ateom.RunWorkloadResponse - 5, // 8: ateom.Ateom.CheckpointWorkload:output_type -> ateom.CheckpointWorkloadResponse - 7, // 9: ateom.Ateom.RestoreWorkload:output_type -> ateom.RestoreWorkloadResponse - 7, // [7:10] is the sub-list for method output_type - 4, // [4:7] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 1, // 0: ateom.RunWorkloadRequest.spec:type_name -> ateom.WorkloadSpec + 8, // 1: ateom.RunWorkloadRequest.runtime_asset_paths:type_name -> ateom.RunWorkloadRequest.RuntimeAssetPathsEntry + 2, // 2: ateom.WorkloadSpec.containers:type_name -> ateom.Container + 1, // 3: ateom.CheckpointWorkloadRequest.spec:type_name -> ateom.WorkloadSpec + 9, // 4: ateom.CheckpointWorkloadRequest.runtime_asset_paths:type_name -> ateom.CheckpointWorkloadRequest.RuntimeAssetPathsEntry + 1, // 5: ateom.RestoreWorkloadRequest.spec:type_name -> ateom.WorkloadSpec + 10, // 6: ateom.RestoreWorkloadRequest.runtime_asset_paths:type_name -> ateom.RestoreWorkloadRequest.RuntimeAssetPathsEntry + 0, // 7: ateom.Ateom.RunWorkload:input_type -> ateom.RunWorkloadRequest + 4, // 8: ateom.Ateom.CheckpointWorkload:input_type -> ateom.CheckpointWorkloadRequest + 6, // 9: ateom.Ateom.RestoreWorkload:input_type -> ateom.RestoreWorkloadRequest + 3, // 10: ateom.Ateom.RunWorkload:output_type -> ateom.RunWorkloadResponse + 5, // 11: ateom.Ateom.CheckpointWorkload:output_type -> ateom.CheckpointWorkloadResponse + 7, // 12: ateom.Ateom.RestoreWorkload:output_type -> ateom.RestoreWorkloadResponse + 10, // [10:13] is the sub-list for method output_type + 7, // [7:10] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_ateom_proto_init() } @@ -579,7 +645,7 @@ func file_ateom_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_ateom_proto_rawDesc), len(file_ateom_proto_rawDesc)), NumEnums: 0, - NumMessages: 8, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/proto/ateompb/ateom.proto b/internal/proto/ateompb/ateom.proto index 1da19e5a3..f88497065 100644 --- a/internal/proto/ateompb/ateom.proto +++ b/internal/proto/ateompb/ateom.proto @@ -55,6 +55,12 @@ message RunWorkloadRequest { string runsc_path = 4; WorkloadSpec spec = 5; + // runtime_asset_paths maps a runtime asset name (e.g. "kata-shim", + // "cloud-hypervisor", "virtiofsd", "kata-kernel", "kata-image", "kata-config") + // to the local on-disk path atelet fetched it to (content-addressed, like + // runsc_path). Empty for the gVisor runtime, which uses runsc_path. + map runtime_asset_paths = 7; + } // WorkloadSpec parallels Pod, but with far fewer configurable fields. @@ -87,10 +93,19 @@ message CheckpointWorkloadRequest { // // For example: "gs://bucket/actors/1234/snapshots/5678/" string snapshot_uri_prefix = 6; + // runtime_asset_paths maps a runtime asset name (e.g. "kata-shim", + // "cloud-hypervisor", "virtiofsd", "kata-kernel", "kata-image", "kata-config") + // to the local on-disk path atelet fetched it to (content-addressed, like + // runsc_path). Empty for the gVisor runtime, which uses runsc_path. + map runtime_asset_paths = 7; + } message CheckpointWorkloadResponse { - + // snapshot_files lists the files ateom wrote into the checkpoint directory + // (relative names) for atelet to ship to object storage. Empty for runtimes + // (e.g. gVisor) that use atelet's legacy fixed file set. + repeated string snapshot_files = 1; } message RestoreWorkloadRequest { @@ -104,6 +119,12 @@ message RestoreWorkloadRequest { // The object storage URI prefix of the snapshot to restore. string snapshot_uri_prefix = 6; + // runtime_asset_paths maps a runtime asset name (e.g. "kata-shim", + // "cloud-hypervisor", "virtiofsd", "kata-kernel", "kata-image", "kata-config") + // to the local on-disk path atelet fetched it to (content-addressed, like + // runsc_path). Empty for the gVisor runtime, which uses runsc_path. + map runtime_asset_paths = 7; + } message RestoreWorkloadResponse { diff --git a/manifests/ate-install/generated/ate.dev_actortemplates.yaml b/manifests/ate-install/generated/ate.dev_actortemplates.yaml index 5f8bd893d..5b1ca8764 100644 --- a/manifests/ate-install/generated/ate.dev_actortemplates.yaml +++ b/manifests/ate-install/generated/ate.dev_actortemplates.yaml @@ -218,6 +218,48 @@ spec: type: object type: object type: object + runtime: + description: |- + Runtime configures a non-gVisor sandbox runtime (e.g. micro-VM). When + Type=microvm, atelet fetches Assets (kata shim, cloud-hypervisor, + virtiofsd, guest kernel/image, base config) instead of runsc. + properties: + assets: + additionalProperties: + description: |- + RuntimeAsset is one content-addressed file atelet fetches for a non-gVisor + runtime. + properties: + sha256Hash: + description: The SHA256 hash of the asset; names the cached + file and verifies integrity. + type: string + url: + description: |- + A gs:// URL pointing to the asset (resolved against the authenticated + client when authentication is set, else the anonymous public client). + type: string + required: + - sha256Hash + - url + type: object + description: Assets maps an asset name to its download config. + type: object + authentication: + description: How should atelet authenticate to fetch the assets? + properties: + gcp: + description: Use GCP application-default credentials. + type: object + type: object + type: + description: Type selects the runtime family. Empty/"gvisor" uses + the runsc path. + enum: + - gvisor + - microvm + type: string + type: object snapshotsConfig: description: Snapshots configuration for the actor. properties: diff --git a/manifests/ate-install/generated/ate.dev_workerpools.yaml b/manifests/ate-install/generated/ate.dev_workerpools.yaml index 634512b23..0e7548f08 100644 --- a/manifests/ate-install/generated/ate.dev_workerpools.yaml +++ b/manifests/ate-install/generated/ate.dev_workerpools.yaml @@ -75,6 +75,16 @@ spec: format: int32 minimum: 0 type: integer + runtime: + default: gvisor + description: |- + Runtime selects the sandbox runtime family, which drives the worker pod + shape (KVM/vhost device mounts and node placement). The concrete binary is + still selected by AteomImage. Defaults to gvisor. + enum: + - gvisor + - microvm + type: string required: - ateomImage - replicas diff --git a/pkg/api/v1alpha1/actortemplate_types.go b/pkg/api/v1alpha1/actortemplate_types.go index 7c1272ca9..b07b4092c 100644 --- a/pkg/api/v1alpha1/actortemplate_types.go +++ b/pkg/api/v1alpha1/actortemplate_types.go @@ -168,6 +168,44 @@ type ActorTemplateSpec struct { // // +required Runsc RunscConfig `json:"runsc,omitempty"` + + // Runtime configures a non-gVisor sandbox runtime (e.g. micro-VM). When + // Type=microvm, atelet fetches Assets (kata shim, cloud-hypervisor, + // virtiofsd, guest kernel/image, base config) instead of runsc. + // + // +optional + Runtime RuntimeConfig `json:"runtime,omitempty"` +} + +// RuntimeAsset is one content-addressed file atelet fetches for a non-gVisor +// runtime. +type RuntimeAsset struct { + // The SHA256 hash of the asset; names the cached file and verifies integrity. + // +required + SHA256Hash string `json:"sha256Hash,omitempty"` + + // A gs:// URL pointing to the asset (resolved against the authenticated + // client when authentication is set, else the anonymous public client). + // +required + URL string `json:"url,omitempty"` +} + +// RuntimeConfig configures a non-gVisor sandbox runtime: the asset set atelet +// fetches, keyed by name (e.g. "kata-shim", "cloud-hypervisor", "virtiofsd", +// "kata-kernel", "kata-image", "kata-config"). +type RuntimeConfig struct { + // Type selects the runtime family. Empty/"gvisor" uses the runsc path. + // +optional + // +kubebuilder:validation:Enum=gvisor;microvm + Type string `json:"type,omitempty"` + + // Assets maps an asset name to its download config. + // +optional + Assets map[string]RuntimeAsset `json:"assets,omitempty"` + + // How should atelet authenticate to fetch the assets? + // +optional + Authentication AuthenticationConfig `json:"authentication,omitempty"` } type GCPAuthenticationConfig struct { diff --git a/pkg/api/v1alpha1/workerpool_types.go b/pkg/api/v1alpha1/workerpool_types.go index 2350432b6..0e011daee 100644 --- a/pkg/api/v1alpha1/workerpool_types.go +++ b/pkg/api/v1alpha1/workerpool_types.go @@ -18,6 +18,19 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// Runtime selects the sandbox runtime family for a WorkerPool. It drives the +// worker pod shape (KVM/vhost devices, node placement); the concrete ateom +// binary is still chosen by AteomImage. +type Runtime string + +const ( + // RuntimeGvisor is the gVisor/runsc runtime (cmd/ateom-gvisor). Default. + RuntimeGvisor Runtime = "gvisor" + // RuntimeMicroVM is the kata + cloud-hypervisor micro-VM runtime + // (cmd/ateom-cloud-hypervisor); needs /dev/kvm and vhost devices. + RuntimeMicroVM Runtime = "microvm" +) + type WorkerPoolSpec struct { // Replicas is the number of worker pods to run. // +required @@ -28,6 +41,14 @@ type WorkerPoolSpec struct { // +kubebuilder:validation:MinLength=1 // +required AteomImage string `json:"ateomImage"` + + // Runtime selects the sandbox runtime family, which drives the worker pod + // shape (KVM/vhost device mounts and node placement). The concrete binary is + // still selected by AteomImage. Defaults to gvisor. + // +optional + // +kubebuilder:validation:Enum=gvisor;microvm + // +kubebuilder:default=gvisor + Runtime Runtime `json:"runtime,omitempty"` } type WorkerPoolStatus struct { diff --git a/pkg/api/v1alpha1/zz_generated.deepcopy.go b/pkg/api/v1alpha1/zz_generated.deepcopy.go index 4bbfe02b6..383fca4f5 100644 --- a/pkg/api/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/v1alpha1/zz_generated.deepcopy.go @@ -95,6 +95,7 @@ func (in *ActorTemplateSpec) DeepCopyInto(out *ActorTemplateSpec) { out.SnapshotsConfig = in.SnapshotsConfig out.WorkerPoolRef = in.WorkerPoolRef in.Runsc.DeepCopyInto(&out.Runsc) + in.Runtime.DeepCopyInto(&out.Runtime) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActorTemplateSpec. @@ -278,6 +279,44 @@ func (in *RunscPlatformConfig) DeepCopy() *RunscPlatformConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RuntimeAsset) DeepCopyInto(out *RuntimeAsset) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeAsset. +func (in *RuntimeAsset) DeepCopy() *RuntimeAsset { + if in == nil { + return nil + } + out := new(RuntimeAsset) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RuntimeConfig) DeepCopyInto(out *RuntimeConfig) { + *out = *in + if in.Assets != nil { + in, out := &in.Assets, &out.Assets + *out = make(map[string]RuntimeAsset, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Authentication.DeepCopyInto(&out.Authentication) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeConfig. +func (in *RuntimeConfig) DeepCopy() *RuntimeConfig { + if in == nil { + return nil + } + out := new(RuntimeConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) { *out = *in diff --git a/vendor/github.com/containerd/containerd/api/LICENSE b/vendor/github.com/containerd/containerd/api/LICENSE new file mode 100644 index 000000000..584149b6e --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright The containerd Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containerd/containerd/api/runtime/task/v2/doc.go b/vendor/github.com/containerd/containerd/api/runtime/task/v2/doc.go new file mode 100644 index 000000000..f933dd8d4 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/runtime/task/v2/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package task diff --git a/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.pb.go b/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.pb.go new file mode 100644 index 000000000..e62f4e70d --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.pb.go @@ -0,0 +1,2331 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: runtime/task/v2/shim.proto + +package task + +import ( + types "github.com/containerd/containerd/api/types" + task "github.com/containerd/containerd/api/types/task" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + emptypb "google.golang.org/protobuf/types/known/emptypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CreateTaskRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + Rootfs []*types.Mount `protobuf:"bytes,3,rep,name=rootfs,proto3" json:"rootfs,omitempty"` + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` + Checkpoint string `protobuf:"bytes,8,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + ParentCheckpoint string `protobuf:"bytes,9,opt,name=parent_checkpoint,json=parentCheckpoint,proto3" json:"parent_checkpoint,omitempty"` + Options *anypb.Any `protobuf:"bytes,10,opt,name=options,proto3" json:"options,omitempty"` +} + +func (x *CreateTaskRequest) Reset() { + *x = CreateTaskRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateTaskRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateTaskRequest) ProtoMessage() {} + +func (x *CreateTaskRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateTaskRequest.ProtoReflect.Descriptor instead. +func (*CreateTaskRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{0} +} + +func (x *CreateTaskRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *CreateTaskRequest) GetBundle() string { + if x != nil { + return x.Bundle + } + return "" +} + +func (x *CreateTaskRequest) GetRootfs() []*types.Mount { + if x != nil { + return x.Rootfs + } + return nil +} + +func (x *CreateTaskRequest) GetTerminal() bool { + if x != nil { + return x.Terminal + } + return false +} + +func (x *CreateTaskRequest) GetStdin() string { + if x != nil { + return x.Stdin + } + return "" +} + +func (x *CreateTaskRequest) GetStdout() string { + if x != nil { + return x.Stdout + } + return "" +} + +func (x *CreateTaskRequest) GetStderr() string { + if x != nil { + return x.Stderr + } + return "" +} + +func (x *CreateTaskRequest) GetCheckpoint() string { + if x != nil { + return x.Checkpoint + } + return "" +} + +func (x *CreateTaskRequest) GetParentCheckpoint() string { + if x != nil { + return x.ParentCheckpoint + } + return "" +} + +func (x *CreateTaskRequest) GetOptions() *anypb.Any { + if x != nil { + return x.Options + } + return nil +} + +type CreateTaskResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (x *CreateTaskResponse) Reset() { + *x = CreateTaskResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateTaskResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateTaskResponse) ProtoMessage() {} + +func (x *CreateTaskResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateTaskResponse.ProtoReflect.Descriptor instead. +func (*CreateTaskResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateTaskResponse) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +type DeleteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (x *DeleteRequest) Reset() { + *x = DeleteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteRequest) ProtoMessage() {} + +func (x *DeleteRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteRequest.ProtoReflect.Descriptor instead. +func (*DeleteRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{2} +} + +func (x *DeleteRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *DeleteRequest) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +type DeleteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + ExitStatus uint32 `protobuf:"varint,2,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=exited_at,json=exitedAt,proto3" json:"exited_at,omitempty"` +} + +func (x *DeleteResponse) Reset() { + *x = DeleteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteResponse) ProtoMessage() {} + +func (x *DeleteResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteResponse.ProtoReflect.Descriptor instead. +func (*DeleteResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{3} +} + +func (x *DeleteResponse) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *DeleteResponse) GetExitStatus() uint32 { + if x != nil { + return x.ExitStatus + } + return 0 +} + +func (x *DeleteResponse) GetExitedAt() *timestamppb.Timestamp { + if x != nil { + return x.ExitedAt + } + return nil +} + +type ExecProcessRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Terminal bool `protobuf:"varint,3,opt,name=terminal,proto3" json:"terminal,omitempty"` + Stdin string `protobuf:"bytes,4,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,5,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,6,opt,name=stderr,proto3" json:"stderr,omitempty"` + Spec *anypb.Any `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` +} + +func (x *ExecProcessRequest) Reset() { + *x = ExecProcessRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecProcessRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecProcessRequest) ProtoMessage() {} + +func (x *ExecProcessRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecProcessRequest.ProtoReflect.Descriptor instead. +func (*ExecProcessRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{4} +} + +func (x *ExecProcessRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *ExecProcessRequest) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +func (x *ExecProcessRequest) GetTerminal() bool { + if x != nil { + return x.Terminal + } + return false +} + +func (x *ExecProcessRequest) GetStdin() string { + if x != nil { + return x.Stdin + } + return "" +} + +func (x *ExecProcessRequest) GetStdout() string { + if x != nil { + return x.Stdout + } + return "" +} + +func (x *ExecProcessRequest) GetStderr() string { + if x != nil { + return x.Stderr + } + return "" +} + +func (x *ExecProcessRequest) GetSpec() *anypb.Any { + if x != nil { + return x.Spec + } + return nil +} + +type ExecProcessResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ExecProcessResponse) Reset() { + *x = ExecProcessResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecProcessResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecProcessResponse) ProtoMessage() {} + +func (x *ExecProcessResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecProcessResponse.ProtoReflect.Descriptor instead. +func (*ExecProcessResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{5} +} + +type ResizePtyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Width uint32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` + Height uint32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` +} + +func (x *ResizePtyRequest) Reset() { + *x = ResizePtyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResizePtyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResizePtyRequest) ProtoMessage() {} + +func (x *ResizePtyRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResizePtyRequest.ProtoReflect.Descriptor instead. +func (*ResizePtyRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{6} +} + +func (x *ResizePtyRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *ResizePtyRequest) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +func (x *ResizePtyRequest) GetWidth() uint32 { + if x != nil { + return x.Width + } + return 0 +} + +func (x *ResizePtyRequest) GetHeight() uint32 { + if x != nil { + return x.Height + } + return 0 +} + +type StateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (x *StateRequest) Reset() { + *x = StateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StateRequest) ProtoMessage() {} + +func (x *StateRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StateRequest.ProtoReflect.Descriptor instead. +func (*StateRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{7} +} + +func (x *StateRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *StateRequest) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +type StateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + Status task.Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"` + Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"` + ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,proto3" json:"exited_at,omitempty"` + ExecID string `protobuf:"bytes,11,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (x *StateResponse) Reset() { + *x = StateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StateResponse) ProtoMessage() {} + +func (x *StateResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StateResponse.ProtoReflect.Descriptor instead. +func (*StateResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{8} +} + +func (x *StateResponse) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *StateResponse) GetBundle() string { + if x != nil { + return x.Bundle + } + return "" +} + +func (x *StateResponse) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *StateResponse) GetStatus() task.Status { + if x != nil { + return x.Status + } + return task.Status(0) +} + +func (x *StateResponse) GetStdin() string { + if x != nil { + return x.Stdin + } + return "" +} + +func (x *StateResponse) GetStdout() string { + if x != nil { + return x.Stdout + } + return "" +} + +func (x *StateResponse) GetStderr() string { + if x != nil { + return x.Stderr + } + return "" +} + +func (x *StateResponse) GetTerminal() bool { + if x != nil { + return x.Terminal + } + return false +} + +func (x *StateResponse) GetExitStatus() uint32 { + if x != nil { + return x.ExitStatus + } + return 0 +} + +func (x *StateResponse) GetExitedAt() *timestamppb.Timestamp { + if x != nil { + return x.ExitedAt + } + return nil +} + +func (x *StateResponse) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +type KillRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Signal uint32 `protobuf:"varint,3,opt,name=signal,proto3" json:"signal,omitempty"` + All bool `protobuf:"varint,4,opt,name=all,proto3" json:"all,omitempty"` +} + +func (x *KillRequest) Reset() { + *x = KillRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *KillRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KillRequest) ProtoMessage() {} + +func (x *KillRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KillRequest.ProtoReflect.Descriptor instead. +func (*KillRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{9} +} + +func (x *KillRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *KillRequest) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +func (x *KillRequest) GetSignal() uint32 { + if x != nil { + return x.Signal + } + return 0 +} + +func (x *KillRequest) GetAll() bool { + if x != nil { + return x.All + } + return false +} + +type CloseIORequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Stdin bool `protobuf:"varint,3,opt,name=stdin,proto3" json:"stdin,omitempty"` +} + +func (x *CloseIORequest) Reset() { + *x = CloseIORequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CloseIORequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloseIORequest) ProtoMessage() {} + +func (x *CloseIORequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CloseIORequest.ProtoReflect.Descriptor instead. +func (*CloseIORequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{10} +} + +func (x *CloseIORequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *CloseIORequest) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +func (x *CloseIORequest) GetStdin() bool { + if x != nil { + return x.Stdin + } + return false +} + +type PidsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *PidsRequest) Reset() { + *x = PidsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PidsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PidsRequest) ProtoMessage() {} + +func (x *PidsRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PidsRequest.ProtoReflect.Descriptor instead. +func (*PidsRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{11} +} + +func (x *PidsRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +type PidsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Processes []*task.ProcessInfo `protobuf:"bytes,1,rep,name=processes,proto3" json:"processes,omitempty"` +} + +func (x *PidsResponse) Reset() { + *x = PidsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PidsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PidsResponse) ProtoMessage() {} + +func (x *PidsResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PidsResponse.ProtoReflect.Descriptor instead. +func (*PidsResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{12} +} + +func (x *PidsResponse) GetProcesses() []*task.ProcessInfo { + if x != nil { + return x.Processes + } + return nil +} + +type CheckpointTaskRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Options *anypb.Any `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"` +} + +func (x *CheckpointTaskRequest) Reset() { + *x = CheckpointTaskRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CheckpointTaskRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckpointTaskRequest) ProtoMessage() {} + +func (x *CheckpointTaskRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CheckpointTaskRequest.ProtoReflect.Descriptor instead. +func (*CheckpointTaskRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{13} +} + +func (x *CheckpointTaskRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *CheckpointTaskRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *CheckpointTaskRequest) GetOptions() *anypb.Any { + if x != nil { + return x.Options + } + return nil +} + +type UpdateTaskRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Resources *anypb.Any `protobuf:"bytes,2,opt,name=resources,proto3" json:"resources,omitempty"` + Annotations map[string]string `protobuf:"bytes,3,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *UpdateTaskRequest) Reset() { + *x = UpdateTaskRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateTaskRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateTaskRequest) ProtoMessage() {} + +func (x *UpdateTaskRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateTaskRequest.ProtoReflect.Descriptor instead. +func (*UpdateTaskRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{14} +} + +func (x *UpdateTaskRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *UpdateTaskRequest) GetResources() *anypb.Any { + if x != nil { + return x.Resources + } + return nil +} + +func (x *UpdateTaskRequest) GetAnnotations() map[string]string { + if x != nil { + return x.Annotations + } + return nil +} + +type StartRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (x *StartRequest) Reset() { + *x = StartRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRequest) ProtoMessage() {} + +func (x *StartRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRequest.ProtoReflect.Descriptor instead. +func (*StartRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{15} +} + +func (x *StartRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *StartRequest) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +type StartResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (x *StartResponse) Reset() { + *x = StartResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartResponse) ProtoMessage() {} + +func (x *StartResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartResponse.ProtoReflect.Descriptor instead. +func (*StartResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{16} +} + +func (x *StartResponse) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +type WaitRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (x *WaitRequest) Reset() { + *x = WaitRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WaitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WaitRequest) ProtoMessage() {} + +func (x *WaitRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WaitRequest.ProtoReflect.Descriptor instead. +func (*WaitRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{17} +} + +func (x *WaitRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *WaitRequest) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +type WaitResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ExitStatus uint32 `protobuf:"varint,1,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=exited_at,json=exitedAt,proto3" json:"exited_at,omitempty"` +} + +func (x *WaitResponse) Reset() { + *x = WaitResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WaitResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WaitResponse) ProtoMessage() {} + +func (x *WaitResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WaitResponse.ProtoReflect.Descriptor instead. +func (*WaitResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{18} +} + +func (x *WaitResponse) GetExitStatus() uint32 { + if x != nil { + return x.ExitStatus + } + return 0 +} + +func (x *WaitResponse) GetExitedAt() *timestamppb.Timestamp { + if x != nil { + return x.ExitedAt + } + return nil +} + +type StatsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *StatsRequest) Reset() { + *x = StatsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatsRequest) ProtoMessage() {} + +func (x *StatsRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatsRequest.ProtoReflect.Descriptor instead. +func (*StatsRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{19} +} + +func (x *StatsRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +type StatsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Stats *anypb.Any `protobuf:"bytes,1,opt,name=stats,proto3" json:"stats,omitempty"` +} + +func (x *StatsResponse) Reset() { + *x = StatsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatsResponse) ProtoMessage() {} + +func (x *StatsResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatsResponse.ProtoReflect.Descriptor instead. +func (*StatsResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{20} +} + +func (x *StatsResponse) GetStats() *anypb.Any { + if x != nil { + return x.Stats + } + return nil +} + +type ConnectRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ConnectRequest) Reset() { + *x = ConnectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectRequest) ProtoMessage() {} + +func (x *ConnectRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectRequest.ProtoReflect.Descriptor instead. +func (*ConnectRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{21} +} + +func (x *ConnectRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +type ConnectResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ShimPid uint32 `protobuf:"varint,1,opt,name=shim_pid,json=shimPid,proto3" json:"shim_pid,omitempty"` + TaskPid uint32 `protobuf:"varint,2,opt,name=task_pid,json=taskPid,proto3" json:"task_pid,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` +} + +func (x *ConnectResponse) Reset() { + *x = ConnectResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectResponse) ProtoMessage() {} + +func (x *ConnectResponse) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectResponse.ProtoReflect.Descriptor instead. +func (*ConnectResponse) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{22} +} + +func (x *ConnectResponse) GetShimPid() uint32 { + if x != nil { + return x.ShimPid + } + return 0 +} + +func (x *ConnectResponse) GetTaskPid() uint32 { + if x != nil { + return x.TaskPid + } + return 0 +} + +func (x *ConnectResponse) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +type ShutdownRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Now bool `protobuf:"varint,2,opt,name=now,proto3" json:"now,omitempty"` +} + +func (x *ShutdownRequest) Reset() { + *x = ShutdownRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ShutdownRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShutdownRequest) ProtoMessage() {} + +func (x *ShutdownRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShutdownRequest.ProtoReflect.Descriptor instead. +func (*ShutdownRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{23} +} + +func (x *ShutdownRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *ShutdownRequest) GetNow() bool { + if x != nil { + return x.Now + } + return false +} + +type PauseRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *PauseRequest) Reset() { + *x = PauseRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PauseRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PauseRequest) ProtoMessage() {} + +func (x *PauseRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PauseRequest.ProtoReflect.Descriptor instead. +func (*PauseRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{24} +} + +func (x *PauseRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +type ResumeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ResumeRequest) Reset() { + *x = ResumeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runtime_task_v2_shim_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResumeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResumeRequest) ProtoMessage() {} + +func (x *ResumeRequest) ProtoReflect() protoreflect.Message { + mi := &file_runtime_task_v2_shim_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResumeRequest.ProtoReflect.Descriptor instead. +func (*ResumeRequest) Descriptor() ([]byte, []int) { + return file_runtime_task_v2_shim_proto_rawDescGZIP(), []int{25} +} + +func (x *ResumeRequest) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +var File_runtime_task_v2_shim_proto protoreflect.FileDescriptor + +var file_runtime_task_v2_shim_proto_rawDesc = []byte{ + 0x0a, 0x1a, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x2f, 0x76, + 0x32, 0x2f, 0x73, 0x68, 0x69, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, + 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0xcb, 0x02, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, + 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x6e, + 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x6e, 0x64, 0x6c, + 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x72, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x74, + 0x66, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x74, 0x64, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x74, 0x64, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, + 0x64, 0x65, 0x72, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x2e, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0x26, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x38, 0x0a, 0x0d, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x78, + 0x65, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x78, 0x65, + 0x63, 0x49, 0x64, 0x22, 0x7c, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x69, 0x74, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x65, 0x78, + 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x22, 0xc9, 0x01, 0x0a, 0x12, 0x45, 0x78, 0x65, 0x63, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x78, 0x65, 0x63, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x78, 0x65, 0x63, 0x49, + 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x74, 0x64, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, + 0x64, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x74, 0x64, 0x65, 0x72, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x64, + 0x65, 0x72, 0x72, 0x12, 0x28, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x15, 0x0a, + 0x13, 0x45, 0x78, 0x65, 0x63, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x50, 0x74, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x78, 0x65, 0x63, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x78, 0x65, 0x63, 0x49, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, + 0x37, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x17, 0x0a, 0x07, 0x65, 0x78, 0x65, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x65, 0x78, 0x65, 0x63, 0x49, 0x64, 0x22, 0xd3, 0x02, 0x0a, 0x0d, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, + 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x6e, 0x64, + 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x03, 0x70, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x64, + 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x64, 0x69, 0x6e, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x64, 0x65, 0x72, + 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x12, + 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x65, + 0x78, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0a, 0x65, 0x78, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x09, + 0x65, 0x78, 0x69, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x78, 0x69, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x78, 0x65, 0x63, 0x5f, 0x69, 0x64, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x78, 0x65, 0x63, 0x49, 0x64, 0x22, 0x60, + 0x0a, 0x0b, 0x4b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, + 0x07, 0x65, 0x78, 0x65, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x65, 0x78, 0x65, 0x63, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, + 0x22, 0x4f, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x49, 0x4f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x78, 0x65, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x78, 0x65, 0x63, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x64, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x74, 0x64, 0x69, + 0x6e, 0x22, 0x1d, 0x0a, 0x0b, 0x50, 0x69, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x4e, 0x0a, 0x0c, 0x50, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3e, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, + 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x22, 0x6b, 0x0a, 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x61, + 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2e, 0x0a, + 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xf1, 0x01, + 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x09, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, + 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x37, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x78, 0x65, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x65, 0x78, 0x65, 0x63, 0x49, 0x64, 0x22, 0x21, 0x0a, 0x0d, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x36, 0x0a, + 0x0b, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, + 0x65, 0x78, 0x65, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, + 0x78, 0x65, 0x63, 0x49, 0x64, 0x22, 0x68, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x65, 0x78, 0x69, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, + 0x1e, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, + 0x3b, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x22, 0x20, 0x0a, 0x0e, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x61, + 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x68, 0x69, 0x6d, 0x50, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, + 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x74, 0x61, 0x73, 0x6b, 0x50, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0x33, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x03, 0x6e, 0x6f, 0x77, 0x22, 0x1e, 0x0a, 0x0c, 0x50, 0x61, 0x75, 0x73, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1f, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0x8a, 0x0a, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, + 0x12, 0x4c, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, + 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, + 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, + 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, + 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, + 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x04, 0x50, 0x69, 0x64, 0x73, 0x12, 0x1f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, + 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x69, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, + 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x41, 0x0a, 0x05, 0x50, 0x61, 0x75, 0x73, 0x65, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, + 0x50, 0x61, 0x75, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x21, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, + 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4f, 0x0a, 0x0a, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x04, 0x4b, 0x69, + 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x4b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x46, 0x0a, 0x04, 0x45, + 0x78, 0x65, 0x63, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, + 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x09, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x50, 0x74, 0x79, + 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, + 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x50, 0x74, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x45, + 0x0a, 0x07, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x49, 0x4f, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x43, + 0x6c, 0x6f, 0x73, 0x65, 0x49, 0x4f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x47, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, + 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, + 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, + 0x0a, 0x04, 0x57, 0x61, 0x69, 0x74, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x61, 0x69, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x61, 0x69, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x05, 0x53, 0x74, 0x61, + 0x74, 0x73, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x08, 0x53, + 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x68, 0x75, + 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x2f, 0x76, 0x32, 0x3b, 0x74, 0x61, 0x73, + 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_runtime_task_v2_shim_proto_rawDescOnce sync.Once + file_runtime_task_v2_shim_proto_rawDescData = file_runtime_task_v2_shim_proto_rawDesc +) + +func file_runtime_task_v2_shim_proto_rawDescGZIP() []byte { + file_runtime_task_v2_shim_proto_rawDescOnce.Do(func() { + file_runtime_task_v2_shim_proto_rawDescData = protoimpl.X.CompressGZIP(file_runtime_task_v2_shim_proto_rawDescData) + }) + return file_runtime_task_v2_shim_proto_rawDescData +} + +var file_runtime_task_v2_shim_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_runtime_task_v2_shim_proto_goTypes = []interface{}{ + (*CreateTaskRequest)(nil), // 0: containerd.task.v2.CreateTaskRequest + (*CreateTaskResponse)(nil), // 1: containerd.task.v2.CreateTaskResponse + (*DeleteRequest)(nil), // 2: containerd.task.v2.DeleteRequest + (*DeleteResponse)(nil), // 3: containerd.task.v2.DeleteResponse + (*ExecProcessRequest)(nil), // 4: containerd.task.v2.ExecProcessRequest + (*ExecProcessResponse)(nil), // 5: containerd.task.v2.ExecProcessResponse + (*ResizePtyRequest)(nil), // 6: containerd.task.v2.ResizePtyRequest + (*StateRequest)(nil), // 7: containerd.task.v2.StateRequest + (*StateResponse)(nil), // 8: containerd.task.v2.StateResponse + (*KillRequest)(nil), // 9: containerd.task.v2.KillRequest + (*CloseIORequest)(nil), // 10: containerd.task.v2.CloseIORequest + (*PidsRequest)(nil), // 11: containerd.task.v2.PidsRequest + (*PidsResponse)(nil), // 12: containerd.task.v2.PidsResponse + (*CheckpointTaskRequest)(nil), // 13: containerd.task.v2.CheckpointTaskRequest + (*UpdateTaskRequest)(nil), // 14: containerd.task.v2.UpdateTaskRequest + (*StartRequest)(nil), // 15: containerd.task.v2.StartRequest + (*StartResponse)(nil), // 16: containerd.task.v2.StartResponse + (*WaitRequest)(nil), // 17: containerd.task.v2.WaitRequest + (*WaitResponse)(nil), // 18: containerd.task.v2.WaitResponse + (*StatsRequest)(nil), // 19: containerd.task.v2.StatsRequest + (*StatsResponse)(nil), // 20: containerd.task.v2.StatsResponse + (*ConnectRequest)(nil), // 21: containerd.task.v2.ConnectRequest + (*ConnectResponse)(nil), // 22: containerd.task.v2.ConnectResponse + (*ShutdownRequest)(nil), // 23: containerd.task.v2.ShutdownRequest + (*PauseRequest)(nil), // 24: containerd.task.v2.PauseRequest + (*ResumeRequest)(nil), // 25: containerd.task.v2.ResumeRequest + nil, // 26: containerd.task.v2.UpdateTaskRequest.AnnotationsEntry + (*types.Mount)(nil), // 27: containerd.types.Mount + (*anypb.Any)(nil), // 28: google.protobuf.Any + (*timestamppb.Timestamp)(nil), // 29: google.protobuf.Timestamp + (task.Status)(0), // 30: containerd.v1.types.Status + (*task.ProcessInfo)(nil), // 31: containerd.v1.types.ProcessInfo + (*emptypb.Empty)(nil), // 32: google.protobuf.Empty +} +var file_runtime_task_v2_shim_proto_depIdxs = []int32{ + 27, // 0: containerd.task.v2.CreateTaskRequest.rootfs:type_name -> containerd.types.Mount + 28, // 1: containerd.task.v2.CreateTaskRequest.options:type_name -> google.protobuf.Any + 29, // 2: containerd.task.v2.DeleteResponse.exited_at:type_name -> google.protobuf.Timestamp + 28, // 3: containerd.task.v2.ExecProcessRequest.spec:type_name -> google.protobuf.Any + 30, // 4: containerd.task.v2.StateResponse.status:type_name -> containerd.v1.types.Status + 29, // 5: containerd.task.v2.StateResponse.exited_at:type_name -> google.protobuf.Timestamp + 31, // 6: containerd.task.v2.PidsResponse.processes:type_name -> containerd.v1.types.ProcessInfo + 28, // 7: containerd.task.v2.CheckpointTaskRequest.options:type_name -> google.protobuf.Any + 28, // 8: containerd.task.v2.UpdateTaskRequest.resources:type_name -> google.protobuf.Any + 26, // 9: containerd.task.v2.UpdateTaskRequest.annotations:type_name -> containerd.task.v2.UpdateTaskRequest.AnnotationsEntry + 29, // 10: containerd.task.v2.WaitResponse.exited_at:type_name -> google.protobuf.Timestamp + 28, // 11: containerd.task.v2.StatsResponse.stats:type_name -> google.protobuf.Any + 7, // 12: containerd.task.v2.Task.State:input_type -> containerd.task.v2.StateRequest + 0, // 13: containerd.task.v2.Task.Create:input_type -> containerd.task.v2.CreateTaskRequest + 15, // 14: containerd.task.v2.Task.Start:input_type -> containerd.task.v2.StartRequest + 2, // 15: containerd.task.v2.Task.Delete:input_type -> containerd.task.v2.DeleteRequest + 11, // 16: containerd.task.v2.Task.Pids:input_type -> containerd.task.v2.PidsRequest + 24, // 17: containerd.task.v2.Task.Pause:input_type -> containerd.task.v2.PauseRequest + 25, // 18: containerd.task.v2.Task.Resume:input_type -> containerd.task.v2.ResumeRequest + 13, // 19: containerd.task.v2.Task.Checkpoint:input_type -> containerd.task.v2.CheckpointTaskRequest + 9, // 20: containerd.task.v2.Task.Kill:input_type -> containerd.task.v2.KillRequest + 4, // 21: containerd.task.v2.Task.Exec:input_type -> containerd.task.v2.ExecProcessRequest + 6, // 22: containerd.task.v2.Task.ResizePty:input_type -> containerd.task.v2.ResizePtyRequest + 10, // 23: containerd.task.v2.Task.CloseIO:input_type -> containerd.task.v2.CloseIORequest + 14, // 24: containerd.task.v2.Task.Update:input_type -> containerd.task.v2.UpdateTaskRequest + 17, // 25: containerd.task.v2.Task.Wait:input_type -> containerd.task.v2.WaitRequest + 19, // 26: containerd.task.v2.Task.Stats:input_type -> containerd.task.v2.StatsRequest + 21, // 27: containerd.task.v2.Task.Connect:input_type -> containerd.task.v2.ConnectRequest + 23, // 28: containerd.task.v2.Task.Shutdown:input_type -> containerd.task.v2.ShutdownRequest + 8, // 29: containerd.task.v2.Task.State:output_type -> containerd.task.v2.StateResponse + 1, // 30: containerd.task.v2.Task.Create:output_type -> containerd.task.v2.CreateTaskResponse + 16, // 31: containerd.task.v2.Task.Start:output_type -> containerd.task.v2.StartResponse + 3, // 32: containerd.task.v2.Task.Delete:output_type -> containerd.task.v2.DeleteResponse + 12, // 33: containerd.task.v2.Task.Pids:output_type -> containerd.task.v2.PidsResponse + 32, // 34: containerd.task.v2.Task.Pause:output_type -> google.protobuf.Empty + 32, // 35: containerd.task.v2.Task.Resume:output_type -> google.protobuf.Empty + 32, // 36: containerd.task.v2.Task.Checkpoint:output_type -> google.protobuf.Empty + 32, // 37: containerd.task.v2.Task.Kill:output_type -> google.protobuf.Empty + 32, // 38: containerd.task.v2.Task.Exec:output_type -> google.protobuf.Empty + 32, // 39: containerd.task.v2.Task.ResizePty:output_type -> google.protobuf.Empty + 32, // 40: containerd.task.v2.Task.CloseIO:output_type -> google.protobuf.Empty + 32, // 41: containerd.task.v2.Task.Update:output_type -> google.protobuf.Empty + 18, // 42: containerd.task.v2.Task.Wait:output_type -> containerd.task.v2.WaitResponse + 20, // 43: containerd.task.v2.Task.Stats:output_type -> containerd.task.v2.StatsResponse + 22, // 44: containerd.task.v2.Task.Connect:output_type -> containerd.task.v2.ConnectResponse + 32, // 45: containerd.task.v2.Task.Shutdown:output_type -> google.protobuf.Empty + 29, // [29:46] is the sub-list for method output_type + 12, // [12:29] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name +} + +func init() { file_runtime_task_v2_shim_proto_init() } +func file_runtime_task_v2_shim_proto_init() { + if File_runtime_task_v2_shim_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_runtime_task_v2_shim_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateTaskRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateTaskResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecProcessRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecProcessResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResizePtyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*KillRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CloseIORequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PidsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PidsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CheckpointTaskRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateTaskRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WaitRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WaitResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShutdownRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PauseRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runtime_task_v2_shim_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResumeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_runtime_task_v2_shim_proto_rawDesc, + NumEnums: 0, + NumMessages: 27, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_runtime_task_v2_shim_proto_goTypes, + DependencyIndexes: file_runtime_task_v2_shim_proto_depIdxs, + MessageInfos: file_runtime_task_v2_shim_proto_msgTypes, + }.Build() + File_runtime_task_v2_shim_proto = out.File + file_runtime_task_v2_shim_proto_rawDesc = nil + file_runtime_task_v2_shim_proto_goTypes = nil + file_runtime_task_v2_shim_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.proto b/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.proto new file mode 100644 index 000000000..6d9c36e03 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.proto @@ -0,0 +1,200 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.task.v2; + +import "google/protobuf/any.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "types/mount.proto"; +import "types/task/task.proto"; + +option go_package = "github.com/containerd/containerd/api/runtime/task/v2;task"; + +// Shim service is launched for each container and is responsible for owning the IO +// for the container and its additional processes. The shim is also the parent of +// each container and allows reattaching to the IO and receiving the exit status +// for the container processes. +service Task { + rpc State(StateRequest) returns (StateResponse); + rpc Create(CreateTaskRequest) returns (CreateTaskResponse); + rpc Start(StartRequest) returns (StartResponse); + rpc Delete(DeleteRequest) returns (DeleteResponse); + rpc Pids(PidsRequest) returns (PidsResponse); + rpc Pause(PauseRequest) returns (google.protobuf.Empty); + rpc Resume(ResumeRequest) returns (google.protobuf.Empty); + rpc Checkpoint(CheckpointTaskRequest) returns (google.protobuf.Empty); + rpc Kill(KillRequest) returns (google.protobuf.Empty); + rpc Exec(ExecProcessRequest) returns (google.protobuf.Empty); + rpc ResizePty(ResizePtyRequest) returns (google.protobuf.Empty); + rpc CloseIO(CloseIORequest) returns (google.protobuf.Empty); + rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty); + rpc Wait(WaitRequest) returns (WaitResponse); + rpc Stats(StatsRequest) returns (StatsResponse); + rpc Connect(ConnectRequest) returns (ConnectResponse); + rpc Shutdown(ShutdownRequest) returns (google.protobuf.Empty); +} + +message CreateTaskRequest { + string id = 1; + string bundle = 2; + repeated containerd.types.Mount rootfs = 3; + bool terminal = 4; + string stdin = 5; + string stdout = 6; + string stderr = 7; + string checkpoint = 8; + string parent_checkpoint = 9; + google.protobuf.Any options = 10; +} + +message CreateTaskResponse { + uint32 pid = 1; +} + +message DeleteRequest { + string id = 1; + string exec_id = 2; +} + +message DeleteResponse { + uint32 pid = 1; + uint32 exit_status = 2; + google.protobuf.Timestamp exited_at = 3; +} + +message ExecProcessRequest { + string id = 1; + string exec_id = 2; + bool terminal = 3; + string stdin = 4; + string stdout = 5; + string stderr = 6; + google.protobuf.Any spec = 7; +} + +message ExecProcessResponse {} + +message ResizePtyRequest { + string id = 1; + string exec_id = 2; + uint32 width = 3; + uint32 height = 4; +} + +message StateRequest { + string id = 1; + string exec_id = 2; +} + +message StateResponse { + string id = 1; + string bundle = 2; + uint32 pid = 3; + containerd.v1.types.Status status = 4; + string stdin = 5; + string stdout = 6; + string stderr = 7; + bool terminal = 8; + uint32 exit_status = 9; + google.protobuf.Timestamp exited_at = 10; + string exec_id = 11; +} + +message KillRequest { + string id = 1; + string exec_id = 2; + uint32 signal = 3; + bool all = 4; +} + +message CloseIORequest { + string id = 1; + string exec_id = 2; + bool stdin = 3; +} + +message PidsRequest { + string id = 1; +} + +message PidsResponse { + repeated containerd.v1.types.ProcessInfo processes = 1; +} + +message CheckpointTaskRequest { + string id = 1; + string path = 2; + google.protobuf.Any options = 3; +} + +message UpdateTaskRequest { + string id = 1; + google.protobuf.Any resources = 2; + map annotations = 3; +} + +message StartRequest { + string id = 1; + string exec_id = 2; +} + +message StartResponse { + uint32 pid = 1; +} + +message WaitRequest { + string id = 1; + string exec_id = 2; +} + +message WaitResponse { + uint32 exit_status = 1; + google.protobuf.Timestamp exited_at = 2; +} + +message StatsRequest { + string id = 1; +} + +message StatsResponse { + google.protobuf.Any stats = 1; +} + +message ConnectRequest { + string id = 1; +} + +message ConnectResponse { + uint32 shim_pid = 1; + uint32 task_pid = 2; + string version = 3; +} + +message ShutdownRequest { + string id = 1; + bool now = 2; +} + +message PauseRequest { + string id = 1; +} + +message ResumeRequest { + string id = 1; +} diff --git a/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim_ttrpc.pb.go b/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim_ttrpc.pb.go new file mode 100644 index 000000000..822d7dadd --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/runtime/task/v2/shim_ttrpc.pb.go @@ -0,0 +1,301 @@ +// Code generated by protoc-gen-go-ttrpc. DO NOT EDIT. +// source: runtime/task/v2/shim.proto +package task + +import ( + context "context" + ttrpc "github.com/containerd/ttrpc" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +type TTRPCTaskService interface { + State(context.Context, *StateRequest) (*StateResponse, error) + Create(context.Context, *CreateTaskRequest) (*CreateTaskResponse, error) + Start(context.Context, *StartRequest) (*StartResponse, error) + Delete(context.Context, *DeleteRequest) (*DeleteResponse, error) + Pids(context.Context, *PidsRequest) (*PidsResponse, error) + Pause(context.Context, *PauseRequest) (*emptypb.Empty, error) + Resume(context.Context, *ResumeRequest) (*emptypb.Empty, error) + Checkpoint(context.Context, *CheckpointTaskRequest) (*emptypb.Empty, error) + Kill(context.Context, *KillRequest) (*emptypb.Empty, error) + Exec(context.Context, *ExecProcessRequest) (*emptypb.Empty, error) + ResizePty(context.Context, *ResizePtyRequest) (*emptypb.Empty, error) + CloseIO(context.Context, *CloseIORequest) (*emptypb.Empty, error) + Update(context.Context, *UpdateTaskRequest) (*emptypb.Empty, error) + Wait(context.Context, *WaitRequest) (*WaitResponse, error) + Stats(context.Context, *StatsRequest) (*StatsResponse, error) + Connect(context.Context, *ConnectRequest) (*ConnectResponse, error) + Shutdown(context.Context, *ShutdownRequest) (*emptypb.Empty, error) +} + +func RegisterTTRPCTaskService(srv *ttrpc.Server, svc TTRPCTaskService) { + srv.RegisterService("containerd.task.v2.Task", &ttrpc.ServiceDesc{ + Methods: map[string]ttrpc.Method{ + "State": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req StateRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.State(ctx, &req) + }, + "Create": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req CreateTaskRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Create(ctx, &req) + }, + "Start": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req StartRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Start(ctx, &req) + }, + "Delete": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req DeleteRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Delete(ctx, &req) + }, + "Pids": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req PidsRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Pids(ctx, &req) + }, + "Pause": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req PauseRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Pause(ctx, &req) + }, + "Resume": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ResumeRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Resume(ctx, &req) + }, + "Checkpoint": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req CheckpointTaskRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Checkpoint(ctx, &req) + }, + "Kill": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req KillRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Kill(ctx, &req) + }, + "Exec": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ExecProcessRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Exec(ctx, &req) + }, + "ResizePty": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ResizePtyRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.ResizePty(ctx, &req) + }, + "CloseIO": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req CloseIORequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.CloseIO(ctx, &req) + }, + "Update": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req UpdateTaskRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Update(ctx, &req) + }, + "Wait": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req WaitRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Wait(ctx, &req) + }, + "Stats": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req StatsRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Stats(ctx, &req) + }, + "Connect": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ConnectRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Connect(ctx, &req) + }, + "Shutdown": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ShutdownRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Shutdown(ctx, &req) + }, + }, + }) +} + +type ttrpctaskClient struct { + client *ttrpc.Client +} + +func NewTTRPCTaskClient(client *ttrpc.Client) TTRPCTaskService { + return &ttrpctaskClient{ + client: client, + } +} + +func (c *ttrpctaskClient) State(ctx context.Context, req *StateRequest) (*StateResponse, error) { + var resp StateResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "State", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Create(ctx context.Context, req *CreateTaskRequest) (*CreateTaskResponse, error) { + var resp CreateTaskResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Create", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Start(ctx context.Context, req *StartRequest) (*StartResponse, error) { + var resp StartResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Start", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) { + var resp DeleteResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Delete", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Pids(ctx context.Context, req *PidsRequest) (*PidsResponse, error) { + var resp PidsResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Pids", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Pause(ctx context.Context, req *PauseRequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Pause", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Resume(ctx context.Context, req *ResumeRequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Resume", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Checkpoint", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Kill(ctx context.Context, req *KillRequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Kill", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Exec(ctx context.Context, req *ExecProcessRequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Exec", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) ResizePty(ctx context.Context, req *ResizePtyRequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "ResizePty", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) CloseIO(ctx context.Context, req *CloseIORequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "CloseIO", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Update(ctx context.Context, req *UpdateTaskRequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Update", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Wait(ctx context.Context, req *WaitRequest) (*WaitResponse, error) { + var resp WaitResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Wait", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Stats(ctx context.Context, req *StatsRequest) (*StatsResponse, error) { + var resp StatsResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Stats", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Connect(ctx context.Context, req *ConnectRequest) (*ConnectResponse, error) { + var resp ConnectResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Connect", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *ttrpctaskClient) Shutdown(ctx context.Context, req *ShutdownRequest) (*emptypb.Empty, error) { + var resp emptypb.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Shutdown", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go b/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go new file mode 100644 index 000000000..ebc43750d --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go @@ -0,0 +1,204 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/descriptor.proto + +package types + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Descriptor describes a blob in a content store. +// +// This descriptor can be used to reference content from an +// oci descriptor found in a manifest. +// See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor +type Descriptor struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MediaType string `protobuf:"bytes,1,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"` + Digest string `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` + Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` + Annotations map[string]string `protobuf:"bytes,5,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Descriptor) Reset() { + *x = Descriptor{} + if protoimpl.UnsafeEnabled { + mi := &file_types_descriptor_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Descriptor) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Descriptor) ProtoMessage() {} + +func (x *Descriptor) ProtoReflect() protoreflect.Message { + mi := &file_types_descriptor_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Descriptor.ProtoReflect.Descriptor instead. +func (*Descriptor) Descriptor() ([]byte, []int) { + return file_types_descriptor_proto_rawDescGZIP(), []int{0} +} + +func (x *Descriptor) GetMediaType() string { + if x != nil { + return x.MediaType + } + return "" +} + +func (x *Descriptor) GetDigest() string { + if x != nil { + return x.Digest + } + return "" +} + +func (x *Descriptor) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *Descriptor) GetAnnotations() map[string]string { + if x != nil { + return x.Annotations + } + return nil +} + +var File_types_descriptor_proto protoreflect.FileDescriptor + +var file_types_descriptor_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0xe8, 0x01, 0x0a, 0x0a, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x64, + 0x69, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, + 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, + 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x12, 0x4f, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x3b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_types_descriptor_proto_rawDescOnce sync.Once + file_types_descriptor_proto_rawDescData = file_types_descriptor_proto_rawDesc +) + +func file_types_descriptor_proto_rawDescGZIP() []byte { + file_types_descriptor_proto_rawDescOnce.Do(func() { + file_types_descriptor_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_descriptor_proto_rawDescData) + }) + return file_types_descriptor_proto_rawDescData +} + +var file_types_descriptor_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_types_descriptor_proto_goTypes = []interface{}{ + (*Descriptor)(nil), // 0: containerd.types.Descriptor + nil, // 1: containerd.types.Descriptor.AnnotationsEntry +} +var file_types_descriptor_proto_depIdxs = []int32{ + 1, // 0: containerd.types.Descriptor.annotations:type_name -> containerd.types.Descriptor.AnnotationsEntry + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_types_descriptor_proto_init() } +func file_types_descriptor_proto_init() { + if File_types_descriptor_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_types_descriptor_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Descriptor); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_descriptor_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_descriptor_proto_goTypes, + DependencyIndexes: file_types_descriptor_proto_depIdxs, + MessageInfos: file_types_descriptor_proto_msgTypes, + }.Build() + File_types_descriptor_proto = out.File + file_types_descriptor_proto_rawDesc = nil + file_types_descriptor_proto_goTypes = nil + file_types_descriptor_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/descriptor.proto b/vendor/github.com/containerd/containerd/api/types/descriptor.proto new file mode 100644 index 000000000..9baadad19 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/descriptor.proto @@ -0,0 +1,33 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.types; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +// Descriptor describes a blob in a content store. +// +// This descriptor can be used to reference content from an +// oci descriptor found in a manifest. +// See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor +message Descriptor { + string media_type = 1; + string digest = 2; + int64 size = 3; + map annotations = 5; +} diff --git a/vendor/github.com/containerd/containerd/api/types/doc.go b/vendor/github.com/containerd/containerd/api/types/doc.go new file mode 100644 index 000000000..475b465ed --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package types diff --git a/vendor/github.com/containerd/containerd/api/types/event.pb.go b/vendor/github.com/containerd/containerd/api/types/event.pb.go new file mode 100644 index 000000000..a1984674d --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/event.pb.go @@ -0,0 +1,205 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/event.proto + +package types + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Envelope struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + Topic string `protobuf:"bytes,3,opt,name=topic,proto3" json:"topic,omitempty"` + Event *anypb.Any `protobuf:"bytes,4,opt,name=event,proto3" json:"event,omitempty"` +} + +func (x *Envelope) Reset() { + *x = Envelope{} + if protoimpl.UnsafeEnabled { + mi := &file_types_event_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Envelope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Envelope) ProtoMessage() {} + +func (x *Envelope) ProtoReflect() protoreflect.Message { + mi := &file_types_event_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Envelope.ProtoReflect.Descriptor instead. +func (*Envelope) Descriptor() ([]byte, []int) { + return file_types_event_proto_rawDescGZIP(), []int{0} +} + +func (x *Envelope) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *Envelope) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *Envelope) GetTopic() string { + if x != nil { + return x.Topic + } + return "" +} + +func (x *Envelope) GetEvent() *anypb.Any { + if x != nil { + return x.Event + } + return nil +} + +var File_types_event_proto protoreflect.FileDescriptor + +var file_types_event_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x15, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x70, 0x61, + 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaa, 0x01, 0x0a, 0x08, 0x45, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, + 0x70, 0x69, 0x63, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, + 0x04, 0x80, 0xb9, 0x1f, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x3b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_types_event_proto_rawDescOnce sync.Once + file_types_event_proto_rawDescData = file_types_event_proto_rawDesc +) + +func file_types_event_proto_rawDescGZIP() []byte { + file_types_event_proto_rawDescOnce.Do(func() { + file_types_event_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_event_proto_rawDescData) + }) + return file_types_event_proto_rawDescData +} + +var file_types_event_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_types_event_proto_goTypes = []interface{}{ + (*Envelope)(nil), // 0: containerd.types.Envelope + (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp + (*anypb.Any)(nil), // 2: google.protobuf.Any +} +var file_types_event_proto_depIdxs = []int32{ + 1, // 0: containerd.types.Envelope.timestamp:type_name -> google.protobuf.Timestamp + 2, // 1: containerd.types.Envelope.event:type_name -> google.protobuf.Any + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_types_event_proto_init() } +func file_types_event_proto_init() { + if File_types_event_proto != nil { + return + } + file_types_fieldpath_proto_init() + if !protoimpl.UnsafeEnabled { + file_types_event_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Envelope); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_event_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_event_proto_goTypes, + DependencyIndexes: file_types_event_proto_depIdxs, + MessageInfos: file_types_event_proto_msgTypes, + }.Build() + File_types_event_proto = out.File + file_types_event_proto_rawDesc = nil + file_types_event_proto_goTypes = nil + file_types_event_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/event.proto b/vendor/github.com/containerd/containerd/api/types/event.proto new file mode 100644 index 000000000..0b9c3fb93 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/event.proto @@ -0,0 +1,33 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.types; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "types/fieldpath.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +message Envelope { + option (containerd.types.fieldpath) = true; + google.protobuf.Timestamp timestamp = 1; + string namespace = 2; + string topic = 3; + google.protobuf.Any event = 4; +} diff --git a/vendor/github.com/containerd/containerd/api/types/fieldpath.pb.go b/vendor/github.com/containerd/containerd/api/types/fieldpath.pb.go new file mode 100644 index 000000000..64172a51e --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/fieldpath.pb.go @@ -0,0 +1,142 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/fieldpath.proto + +package types + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +var file_types_fieldpath_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptorpb.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63300, + Name: "containerd.types.fieldpath_all", + Tag: "varint,63300,opt,name=fieldpath_all", + Filename: "types/fieldpath.proto", + }, + { + ExtendedType: (*descriptorpb.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64400, + Name: "containerd.types.fieldpath", + Tag: "varint,64400,opt,name=fieldpath", + Filename: "types/fieldpath.proto", + }, +} + +// Extension fields to descriptorpb.FileOptions. +var ( + // optional bool fieldpath_all = 63300; + E_FieldpathAll = &file_types_fieldpath_proto_extTypes[0] +) + +// Extension fields to descriptorpb.MessageOptions. +var ( + // optional bool fieldpath = 64400; + E_Fieldpath = &file_types_fieldpath_proto_extTypes[1] +) + +var File_types_fieldpath_proto protoreflect.FileDescriptor + +var file_types_fieldpath_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x70, 0x61, 0x74, + 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x46, 0x0a, 0x0d, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x61, 0x6c, 0x6c, 0x12, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, + 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc4, 0xee, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x70, 0x61, 0x74, 0x68, 0x41, 0x6c, 0x6c, + 0x88, 0x01, 0x01, 0x3a, 0x42, 0x0a, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x70, 0x61, 0x74, 0x68, + 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x90, 0xf7, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, + 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x3b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var file_types_fieldpath_proto_goTypes = []interface{}{ + (*descriptorpb.FileOptions)(nil), // 0: google.protobuf.FileOptions + (*descriptorpb.MessageOptions)(nil), // 1: google.protobuf.MessageOptions +} +var file_types_fieldpath_proto_depIdxs = []int32{ + 0, // 0: containerd.types.fieldpath_all:extendee -> google.protobuf.FileOptions + 1, // 1: containerd.types.fieldpath:extendee -> google.protobuf.MessageOptions + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 0, // [0:2] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_types_fieldpath_proto_init() } +func file_types_fieldpath_proto_init() { + if File_types_fieldpath_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_fieldpath_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 2, + NumServices: 0, + }, + GoTypes: file_types_fieldpath_proto_goTypes, + DependencyIndexes: file_types_fieldpath_proto_depIdxs, + ExtensionInfos: file_types_fieldpath_proto_extTypes, + }.Build() + File_types_fieldpath_proto = out.File + file_types_fieldpath_proto_rawDesc = nil + file_types_fieldpath_proto_goTypes = nil + file_types_fieldpath_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/fieldpath.proto b/vendor/github.com/containerd/containerd/api/types/fieldpath.proto new file mode 100644 index 000000000..bb5591da5 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/fieldpath.proto @@ -0,0 +1,42 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; +package containerd.types; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +extend google.protobuf.FileOptions { + optional bool fieldpath_all = 63300; +} + +extend google.protobuf.MessageOptions { + optional bool fieldpath = 64400; +} diff --git a/vendor/github.com/containerd/containerd/api/types/introspection.pb.go b/vendor/github.com/containerd/containerd/api/types/introspection.pb.go new file mode 100644 index 000000000..b5f804473 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/introspection.pb.go @@ -0,0 +1,373 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/introspection.proto + +package types + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RuntimeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RuntimePath string `protobuf:"bytes,1,opt,name=runtime_path,json=runtimePath,proto3" json:"runtime_path,omitempty"` + // Options correspond to CreateTaskRequest.options. + // This is needed to pass the runc binary path, etc. + Options *anypb.Any `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` +} + +func (x *RuntimeRequest) Reset() { + *x = RuntimeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_types_introspection_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RuntimeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RuntimeRequest) ProtoMessage() {} + +func (x *RuntimeRequest) ProtoReflect() protoreflect.Message { + mi := &file_types_introspection_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RuntimeRequest.ProtoReflect.Descriptor instead. +func (*RuntimeRequest) Descriptor() ([]byte, []int) { + return file_types_introspection_proto_rawDescGZIP(), []int{0} +} + +func (x *RuntimeRequest) GetRuntimePath() string { + if x != nil { + return x.RuntimePath + } + return "" +} + +func (x *RuntimeRequest) GetOptions() *anypb.Any { + if x != nil { + return x.Options + } + return nil +} + +type RuntimeVersion struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"` +} + +func (x *RuntimeVersion) Reset() { + *x = RuntimeVersion{} + if protoimpl.UnsafeEnabled { + mi := &file_types_introspection_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RuntimeVersion) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RuntimeVersion) ProtoMessage() {} + +func (x *RuntimeVersion) ProtoReflect() protoreflect.Message { + mi := &file_types_introspection_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RuntimeVersion.ProtoReflect.Descriptor instead. +func (*RuntimeVersion) Descriptor() ([]byte, []int) { + return file_types_introspection_proto_rawDescGZIP(), []int{1} +} + +func (x *RuntimeVersion) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *RuntimeVersion) GetRevision() string { + if x != nil { + return x.Revision + } + return "" +} + +type RuntimeInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Version *RuntimeVersion `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + // Options correspond to RuntimeInfoRequest.Options (contains runc binary path, etc.) + Options *anypb.Any `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"` + // OCI-compatible runtimes should use https://github.com/opencontainers/runtime-spec/blob/main/features.md + Features *anypb.Any `protobuf:"bytes,4,opt,name=features,proto3" json:"features,omitempty"` + // Annotations of the shim. Irrelevant to features.Annotations. + Annotations map[string]string `protobuf:"bytes,5,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *RuntimeInfo) Reset() { + *x = RuntimeInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_types_introspection_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RuntimeInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RuntimeInfo) ProtoMessage() {} + +func (x *RuntimeInfo) ProtoReflect() protoreflect.Message { + mi := &file_types_introspection_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RuntimeInfo.ProtoReflect.Descriptor instead. +func (*RuntimeInfo) Descriptor() ([]byte, []int) { + return file_types_introspection_proto_rawDescGZIP(), []int{2} +} + +func (x *RuntimeInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *RuntimeInfo) GetVersion() *RuntimeVersion { + if x != nil { + return x.Version + } + return nil +} + +func (x *RuntimeInfo) GetOptions() *anypb.Any { + if x != nil { + return x.Options + } + return nil +} + +func (x *RuntimeInfo) GetFeatures() *anypb.Any { + if x != nil { + return x.Features + } + return nil +} + +func (x *RuntimeInfo) GetAnnotations() map[string]string { + if x != nil { + return x.Annotations + } + return nil +} + +var File_types_introspection_proto protoreflect.FileDescriptor + +var file_types_introspection_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x19, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, + 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x63, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2e, 0x0a, + 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x46, 0x0a, + 0x0e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd1, 0x02, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x50, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_types_introspection_proto_rawDescOnce sync.Once + file_types_introspection_proto_rawDescData = file_types_introspection_proto_rawDesc +) + +func file_types_introspection_proto_rawDescGZIP() []byte { + file_types_introspection_proto_rawDescOnce.Do(func() { + file_types_introspection_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_introspection_proto_rawDescData) + }) + return file_types_introspection_proto_rawDescData +} + +var file_types_introspection_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_types_introspection_proto_goTypes = []interface{}{ + (*RuntimeRequest)(nil), // 0: containerd.types.RuntimeRequest + (*RuntimeVersion)(nil), // 1: containerd.types.RuntimeVersion + (*RuntimeInfo)(nil), // 2: containerd.types.RuntimeInfo + nil, // 3: containerd.types.RuntimeInfo.AnnotationsEntry + (*anypb.Any)(nil), // 4: google.protobuf.Any +} +var file_types_introspection_proto_depIdxs = []int32{ + 4, // 0: containerd.types.RuntimeRequest.options:type_name -> google.protobuf.Any + 1, // 1: containerd.types.RuntimeInfo.version:type_name -> containerd.types.RuntimeVersion + 4, // 2: containerd.types.RuntimeInfo.options:type_name -> google.protobuf.Any + 4, // 3: containerd.types.RuntimeInfo.features:type_name -> google.protobuf.Any + 3, // 4: containerd.types.RuntimeInfo.annotations:type_name -> containerd.types.RuntimeInfo.AnnotationsEntry + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_types_introspection_proto_init() } +func file_types_introspection_proto_init() { + if File_types_introspection_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_types_introspection_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RuntimeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_types_introspection_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RuntimeVersion); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_types_introspection_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RuntimeInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_introspection_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_introspection_proto_goTypes, + DependencyIndexes: file_types_introspection_proto_depIdxs, + MessageInfos: file_types_introspection_proto_msgTypes, + }.Build() + File_types_introspection_proto = out.File + file_types_introspection_proto_rawDesc = nil + file_types_introspection_proto_goTypes = nil + file_types_introspection_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/introspection.proto b/vendor/github.com/containerd/containerd/api/types/introspection.proto new file mode 100644 index 000000000..5ce83bfe0 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/introspection.proto @@ -0,0 +1,46 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.types; + +import "google/protobuf/any.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +message RuntimeRequest { + string runtime_path = 1; + // Options correspond to CreateTaskRequest.options. + // This is needed to pass the runc binary path, etc. + google.protobuf.Any options = 2; +} + +message RuntimeVersion { + string version = 1; + string revision = 2; +} + +message RuntimeInfo { + string name = 1; + RuntimeVersion version = 2; + // Options correspond to RuntimeInfoRequest.Options (contains runc binary path, etc.) + google.protobuf.Any options = 3; + // OCI-compatible runtimes should use https://github.com/opencontainers/runtime-spec/blob/main/features.md + google.protobuf.Any features = 4; + // Annotations of the shim. Irrelevant to features.Annotations. + map annotations = 5; +} diff --git a/vendor/github.com/containerd/containerd/api/types/metrics.pb.go b/vendor/github.com/containerd/containerd/api/types/metrics.pb.go new file mode 100644 index 000000000..1cd6a7548 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/metrics.pb.go @@ -0,0 +1,191 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/metrics.proto + +package types + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Metric struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Data *anypb.Any `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Metric) Reset() { + *x = Metric{} + if protoimpl.UnsafeEnabled { + mi := &file_types_metrics_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Metric) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Metric) ProtoMessage() {} + +func (x *Metric) ProtoReflect() protoreflect.Message { + mi := &file_types_metrics_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Metric.ProtoReflect.Descriptor instead. +func (*Metric) Descriptor() ([]byte, []int) { + return file_types_metrics_proto_rawDescGZIP(), []int{0} +} + +func (x *Metric) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *Metric) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *Metric) GetData() *anypb.Any { + if x != nil { + return x.Data + } + return nil +} + +var File_types_metrics_proto protoreflect.FileDescriptor + +var file_types_metrics_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x7c, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x38, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3b, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_types_metrics_proto_rawDescOnce sync.Once + file_types_metrics_proto_rawDescData = file_types_metrics_proto_rawDesc +) + +func file_types_metrics_proto_rawDescGZIP() []byte { + file_types_metrics_proto_rawDescOnce.Do(func() { + file_types_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_metrics_proto_rawDescData) + }) + return file_types_metrics_proto_rawDescData +} + +var file_types_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_types_metrics_proto_goTypes = []interface{}{ + (*Metric)(nil), // 0: containerd.types.Metric + (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp + (*anypb.Any)(nil), // 2: google.protobuf.Any +} +var file_types_metrics_proto_depIdxs = []int32{ + 1, // 0: containerd.types.Metric.timestamp:type_name -> google.protobuf.Timestamp + 2, // 1: containerd.types.Metric.data:type_name -> google.protobuf.Any + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_types_metrics_proto_init() } +func file_types_metrics_proto_init() { + if File_types_metrics_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_types_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Metric); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_metrics_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_metrics_proto_goTypes, + DependencyIndexes: file_types_metrics_proto_depIdxs, + MessageInfos: file_types_metrics_proto_msgTypes, + }.Build() + File_types_metrics_proto = out.File + file_types_metrics_proto_rawDesc = nil + file_types_metrics_proto_goTypes = nil + file_types_metrics_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/metrics.proto b/vendor/github.com/containerd/containerd/api/types/metrics.proto new file mode 100644 index 000000000..d1a7d49cc --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/metrics.proto @@ -0,0 +1,30 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.types; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +message Metric { + google.protobuf.Timestamp timestamp = 1; + string id = 2; + google.protobuf.Any data = 3; +} diff --git a/vendor/github.com/containerd/containerd/api/types/mount.pb.go b/vendor/github.com/containerd/containerd/api/types/mount.pb.go new file mode 100644 index 000000000..5191270fe --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/mount.pb.go @@ -0,0 +1,414 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/mount.proto + +package types + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Mount describes mounts for a container. +// +// This type is the lingua franca of ContainerD. All services provide mounts +// to be used with the container at creation time. +// +// The Mount type follows the structure of the mount syscall, including a type, +// source, target and options. +type Mount struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Type defines the nature of the mount. + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // Source specifies the name of the mount. Depending on mount type, this + // may be a volume name or a host path, or even ignored. + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` + // Target path in container + Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` + // Options specifies zero or more fstab style mount options. + Options []string `protobuf:"bytes,4,rep,name=options,proto3" json:"options,omitempty"` +} + +func (x *Mount) Reset() { + *x = Mount{} + if protoimpl.UnsafeEnabled { + mi := &file_types_mount_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Mount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Mount) ProtoMessage() {} + +func (x *Mount) ProtoReflect() protoreflect.Message { + mi := &file_types_mount_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Mount.ProtoReflect.Descriptor instead. +func (*Mount) Descriptor() ([]byte, []int) { + return file_types_mount_proto_rawDescGZIP(), []int{0} +} + +func (x *Mount) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Mount) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *Mount) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *Mount) GetOptions() []string { + if x != nil { + return x.Options + } + return nil +} + +type ActiveMount struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Mount *Mount `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + MountedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=mounted_at,json=mountedAt,proto3" json:"mounted_at,omitempty"` + MountPoint string `protobuf:"bytes,3,opt,name=mount_point,json=mountPoint,proto3" json:"mount_point,omitempty"` + Data map[string]string `protobuf:"bytes,4,rep,name=data,proto3" json:"data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ActiveMount) Reset() { + *x = ActiveMount{} + if protoimpl.UnsafeEnabled { + mi := &file_types_mount_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActiveMount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActiveMount) ProtoMessage() {} + +func (x *ActiveMount) ProtoReflect() protoreflect.Message { + mi := &file_types_mount_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActiveMount.ProtoReflect.Descriptor instead. +func (*ActiveMount) Descriptor() ([]byte, []int) { + return file_types_mount_proto_rawDescGZIP(), []int{1} +} + +func (x *ActiveMount) GetMount() *Mount { + if x != nil { + return x.Mount + } + return nil +} + +func (x *ActiveMount) GetMountedAt() *timestamppb.Timestamp { + if x != nil { + return x.MountedAt + } + return nil +} + +func (x *ActiveMount) GetMountPoint() string { + if x != nil { + return x.MountPoint + } + return "" +} + +func (x *ActiveMount) GetData() map[string]string { + if x != nil { + return x.Data + } + return nil +} + +type ActivationInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Active []*ActiveMount `protobuf:"bytes,2,rep,name=active,proto3" json:"active,omitempty"` + System []*Mount `protobuf:"bytes,3,rep,name=system,proto3" json:"system,omitempty"` + Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ActivationInfo) Reset() { + *x = ActivationInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_types_mount_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActivationInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActivationInfo) ProtoMessage() {} + +func (x *ActivationInfo) ProtoReflect() protoreflect.Message { + mi := &file_types_mount_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActivationInfo.ProtoReflect.Descriptor instead. +func (*ActivationInfo) Descriptor() ([]byte, []int) { + return file_types_mount_proto_rawDescGZIP(), []int{2} +} + +func (x *ActivationInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ActivationInfo) GetActive() []*ActiveMount { + if x != nil { + return x.Active + } + return nil +} + +func (x *ActivationInfo) GetSystem() []*Mount { + if x != nil { + return x.System + } + return nil +} + +func (x *ActivationInfo) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + +var File_types_mount_proto protoreflect.FileDescriptor + +var file_types_mount_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x65, 0x0a, 0x05, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, + 0x0a, 0x0b, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2d, 0x0a, + 0x05, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x05, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8d, + 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4d, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x2f, 0x0a, 0x06, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x44, 0x0a, + 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x2e, + 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x32, + 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3b, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_types_mount_proto_rawDescOnce sync.Once + file_types_mount_proto_rawDescData = file_types_mount_proto_rawDesc +) + +func file_types_mount_proto_rawDescGZIP() []byte { + file_types_mount_proto_rawDescOnce.Do(func() { + file_types_mount_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_mount_proto_rawDescData) + }) + return file_types_mount_proto_rawDescData +} + +var file_types_mount_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_types_mount_proto_goTypes = []interface{}{ + (*Mount)(nil), // 0: containerd.types.Mount + (*ActiveMount)(nil), // 1: containerd.types.ActiveMount + (*ActivationInfo)(nil), // 2: containerd.types.ActivationInfo + nil, // 3: containerd.types.ActiveMount.DataEntry + nil, // 4: containerd.types.ActivationInfo.LabelsEntry + (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp +} +var file_types_mount_proto_depIdxs = []int32{ + 0, // 0: containerd.types.ActiveMount.mount:type_name -> containerd.types.Mount + 5, // 1: containerd.types.ActiveMount.mounted_at:type_name -> google.protobuf.Timestamp + 3, // 2: containerd.types.ActiveMount.data:type_name -> containerd.types.ActiveMount.DataEntry + 1, // 3: containerd.types.ActivationInfo.active:type_name -> containerd.types.ActiveMount + 0, // 4: containerd.types.ActivationInfo.system:type_name -> containerd.types.Mount + 4, // 5: containerd.types.ActivationInfo.labels:type_name -> containerd.types.ActivationInfo.LabelsEntry + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_types_mount_proto_init() } +func file_types_mount_proto_init() { + if File_types_mount_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_types_mount_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Mount); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_types_mount_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActiveMount); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_types_mount_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActivationInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_mount_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_mount_proto_goTypes, + DependencyIndexes: file_types_mount_proto_depIdxs, + MessageInfos: file_types_mount_proto_msgTypes, + }.Build() + File_types_mount_proto = out.File + file_types_mount_proto_rawDesc = nil + file_types_mount_proto_goTypes = nil + file_types_mount_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/mount.proto b/vendor/github.com/containerd/containerd/api/types/mount.proto new file mode 100644 index 000000000..05fd4532b --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/mount.proto @@ -0,0 +1,65 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.types; + +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +// Mount describes mounts for a container. +// +// This type is the lingua franca of ContainerD. All services provide mounts +// to be used with the container at creation time. +// +// The Mount type follows the structure of the mount syscall, including a type, +// source, target and options. +message Mount { + // Type defines the nature of the mount. + string type = 1; + + // Source specifies the name of the mount. Depending on mount type, this + // may be a volume name or a host path, or even ignored. + string source = 2; + + // Target path in container + string target = 3; + + // Options specifies zero or more fstab style mount options. + repeated string options = 4; +} + +message ActiveMount { + Mount mount = 1; + + google.protobuf.Timestamp mounted_at = 2; + + string mount_point = 3; + + map data = 4; +} + +message ActivationInfo { + string name = 1; + + repeated ActiveMount active = 2; + + repeated Mount system = 3; + + map labels = 4; +} diff --git a/vendor/github.com/containerd/containerd/api/types/platform.pb.go b/vendor/github.com/containerd/containerd/api/types/platform.pb.go new file mode 100644 index 000000000..947b520db --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/platform.pb.go @@ -0,0 +1,201 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/platform.proto + +package types + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Platform follows the structure of the OCI platform specification, from +// descriptors. +type Platform struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OS string `protobuf:"bytes,1,opt,name=os,proto3" json:"os,omitempty"` + Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"` + Variant string `protobuf:"bytes,3,opt,name=variant,proto3" json:"variant,omitempty"` + OSVersion string `protobuf:"bytes,4,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"` + OSFeatures []string `protobuf:"bytes,5,rep,name=os_features,json=osFeatures,proto3" json:"os_features,omitempty"` +} + +func (x *Platform) Reset() { + *x = Platform{} + if protoimpl.UnsafeEnabled { + mi := &file_types_platform_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Platform) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Platform) ProtoMessage() {} + +func (x *Platform) ProtoReflect() protoreflect.Message { + mi := &file_types_platform_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Platform.ProtoReflect.Descriptor instead. +func (*Platform) Descriptor() ([]byte, []int) { + return file_types_platform_proto_rawDescGZIP(), []int{0} +} + +func (x *Platform) GetOS() string { + if x != nil { + return x.OS + } + return "" +} + +func (x *Platform) GetArchitecture() string { + if x != nil { + return x.Architecture + } + return "" +} + +func (x *Platform) GetVariant() string { + if x != nil { + return x.Variant + } + return "" +} + +func (x *Platform) GetOsVersion() string { + if x != nil { + return x.OSVersion + } + return "" +} + +func (x *Platform) GetOsFeatures() []string { + if x != nil { + return x.OSFeatures + } + return nil +} + +var File_types_platform_proto protoreflect.FileDescriptor + +var file_types_platform_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x08, 0x50, 0x6c, 0x61, + 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, + 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, + 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x61, 0x72, + 0x69, 0x61, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x73, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x3b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_types_platform_proto_rawDescOnce sync.Once + file_types_platform_proto_rawDescData = file_types_platform_proto_rawDesc +) + +func file_types_platform_proto_rawDescGZIP() []byte { + file_types_platform_proto_rawDescOnce.Do(func() { + file_types_platform_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_platform_proto_rawDescData) + }) + return file_types_platform_proto_rawDescData +} + +var file_types_platform_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_types_platform_proto_goTypes = []interface{}{ + (*Platform)(nil), // 0: containerd.types.Platform +} +var file_types_platform_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_types_platform_proto_init() } +func file_types_platform_proto_init() { + if File_types_platform_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_types_platform_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Platform); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_platform_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_platform_proto_goTypes, + DependencyIndexes: file_types_platform_proto_depIdxs, + MessageInfos: file_types_platform_proto_msgTypes, + }.Build() + File_types_platform_proto = out.File + file_types_platform_proto_rawDesc = nil + file_types_platform_proto_goTypes = nil + file_types_platform_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/platform.proto b/vendor/github.com/containerd/containerd/api/types/platform.proto new file mode 100644 index 000000000..33ed85301 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/platform.proto @@ -0,0 +1,31 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.types; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +// Platform follows the structure of the OCI platform specification, from +// descriptors. +message Platform { + string os = 1; + string architecture = 2; + string variant = 3; + string os_version = 4; + repeated string os_features = 5; +} diff --git a/vendor/github.com/containerd/containerd/api/types/platform_helpers.go b/vendor/github.com/containerd/containerd/api/types/platform_helpers.go new file mode 100644 index 000000000..ba1fec577 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/platform_helpers.go @@ -0,0 +1,51 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package types + +import oci "github.com/opencontainers/image-spec/specs-go/v1" + +// OCIPlatformToProto converts from a slice of OCI [specs.Platform] to a +// slice of the protobuf definition [Platform]. +func OCIPlatformToProto(platforms []oci.Platform) []*Platform { + ap := make([]*Platform, len(platforms)) + for i := range platforms { + ap[i] = &Platform{ + OS: platforms[i].OS, + OSVersion: platforms[i].OSVersion, + Architecture: platforms[i].Architecture, + Variant: platforms[i].Variant, + OSFeatures: platforms[i].OSFeatures, + } + } + return ap +} + +// OCIPlatformFromProto converts a slice of the protobuf definition [Platform] +// to a slice of OCI [specs.Platform]. +func OCIPlatformFromProto(platforms []*Platform) []oci.Platform { + op := make([]oci.Platform, len(platforms)) + for i := range platforms { + op[i] = oci.Platform{ + OS: platforms[i].OS, + OSVersion: platforms[i].OSVersion, + Architecture: platforms[i].Architecture, + Variant: platforms[i].Variant, + OSFeatures: platforms[i].OSFeatures, + } + } + return op +} diff --git a/vendor/github.com/containerd/containerd/api/types/sandbox.pb.go b/vendor/github.com/containerd/containerd/api/types/sandbox.pb.go new file mode 100644 index 000000000..eb84c7be6 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/sandbox.pb.go @@ -0,0 +1,354 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/sandbox.proto + +package types + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Sandbox represents a sandbox metadata object that keeps all info required by controller to +// work with a particular instance. +type Sandbox struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // SandboxID is a unique instance identifier within namespace + SandboxID string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` + // Runtime specifies which runtime to use for executing this container. + Runtime *Sandbox_Runtime `protobuf:"bytes,2,opt,name=runtime,proto3" json:"runtime,omitempty"` + // Spec is sandbox configuration (kin of OCI runtime spec), spec's data will be written to a config.json file in the + // bundle directory (similary to OCI spec). + Spec *anypb.Any `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` + // Labels provides an area to include arbitrary data on containers. + Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // CreatedAt is the time the container was first created. + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + // UpdatedAt is the last time the container was mutated. + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + // Extensions allow clients to provide optional blobs that can be handled by runtime. + Extensions map[string]*anypb.Any `protobuf:"bytes,7,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Sandboxer is the name of the sandbox controller who manages the sandbox. + Sandboxer string `protobuf:"bytes,10,opt,name=sandboxer,proto3" json:"sandboxer,omitempty"` +} + +func (x *Sandbox) Reset() { + *x = Sandbox{} + if protoimpl.UnsafeEnabled { + mi := &file_types_sandbox_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Sandbox) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Sandbox) ProtoMessage() {} + +func (x *Sandbox) ProtoReflect() protoreflect.Message { + mi := &file_types_sandbox_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Sandbox.ProtoReflect.Descriptor instead. +func (*Sandbox) Descriptor() ([]byte, []int) { + return file_types_sandbox_proto_rawDescGZIP(), []int{0} +} + +func (x *Sandbox) GetSandboxID() string { + if x != nil { + return x.SandboxID + } + return "" +} + +func (x *Sandbox) GetRuntime() *Sandbox_Runtime { + if x != nil { + return x.Runtime + } + return nil +} + +func (x *Sandbox) GetSpec() *anypb.Any { + if x != nil { + return x.Spec + } + return nil +} + +func (x *Sandbox) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + +func (x *Sandbox) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Sandbox) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +func (x *Sandbox) GetExtensions() map[string]*anypb.Any { + if x != nil { + return x.Extensions + } + return nil +} + +func (x *Sandbox) GetSandboxer() string { + if x != nil { + return x.Sandboxer + } + return "" +} + +type Sandbox_Runtime struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Name is the name of the runtime. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Options specify additional runtime initialization options for the shim (this data will be available in StartShim). + // Typically this data expected to be runtime shim implementation specific. + Options *anypb.Any `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` +} + +func (x *Sandbox_Runtime) Reset() { + *x = Sandbox_Runtime{} + if protoimpl.UnsafeEnabled { + mi := &file_types_sandbox_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Sandbox_Runtime) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Sandbox_Runtime) ProtoMessage() {} + +func (x *Sandbox_Runtime) ProtoReflect() protoreflect.Message { + mi := &file_types_sandbox_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Sandbox_Runtime.ProtoReflect.Descriptor instead. +func (*Sandbox_Runtime) Descriptor() ([]byte, []int) { + return file_types_sandbox_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *Sandbox_Runtime) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Sandbox_Runtime) GetOptions() *anypb.Any { + if x != nil { + return x.Options + } + return nil +} + +var File_types_sandbox_proto protoreflect.FileDescriptor + +var file_types_sandbox_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x73, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x8c, 0x05, 0x0a, 0x07, 0x53, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x3b, + 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2e, 0x53, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x73, + 0x70, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x3d, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, + 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x49, 0x0a, 0x0a, 0x65, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x53, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, + 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x61, 0x6e, 0x64, 0x62, 0x6f, + 0x78, 0x65, 0x72, 0x1a, 0x4d, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x53, 0x0a, + 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x3b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_types_sandbox_proto_rawDescOnce sync.Once + file_types_sandbox_proto_rawDescData = file_types_sandbox_proto_rawDesc +) + +func file_types_sandbox_proto_rawDescGZIP() []byte { + file_types_sandbox_proto_rawDescOnce.Do(func() { + file_types_sandbox_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_sandbox_proto_rawDescData) + }) + return file_types_sandbox_proto_rawDescData +} + +var file_types_sandbox_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_types_sandbox_proto_goTypes = []interface{}{ + (*Sandbox)(nil), // 0: containerd.types.Sandbox + (*Sandbox_Runtime)(nil), // 1: containerd.types.Sandbox.Runtime + nil, // 2: containerd.types.Sandbox.LabelsEntry + nil, // 3: containerd.types.Sandbox.ExtensionsEntry + (*anypb.Any)(nil), // 4: google.protobuf.Any + (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp +} +var file_types_sandbox_proto_depIdxs = []int32{ + 1, // 0: containerd.types.Sandbox.runtime:type_name -> containerd.types.Sandbox.Runtime + 4, // 1: containerd.types.Sandbox.spec:type_name -> google.protobuf.Any + 2, // 2: containerd.types.Sandbox.labels:type_name -> containerd.types.Sandbox.LabelsEntry + 5, // 3: containerd.types.Sandbox.created_at:type_name -> google.protobuf.Timestamp + 5, // 4: containerd.types.Sandbox.updated_at:type_name -> google.protobuf.Timestamp + 3, // 5: containerd.types.Sandbox.extensions:type_name -> containerd.types.Sandbox.ExtensionsEntry + 4, // 6: containerd.types.Sandbox.Runtime.options:type_name -> google.protobuf.Any + 4, // 7: containerd.types.Sandbox.ExtensionsEntry.value:type_name -> google.protobuf.Any + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_types_sandbox_proto_init() } +func file_types_sandbox_proto_init() { + if File_types_sandbox_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_types_sandbox_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Sandbox); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_types_sandbox_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Sandbox_Runtime); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_sandbox_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_sandbox_proto_goTypes, + DependencyIndexes: file_types_sandbox_proto_depIdxs, + MessageInfos: file_types_sandbox_proto_msgTypes, + }.Build() + File_types_sandbox_proto = out.File + file_types_sandbox_proto_rawDesc = nil + file_types_sandbox_proto_goTypes = nil + file_types_sandbox_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/sandbox.proto b/vendor/github.com/containerd/containerd/api/types/sandbox.proto new file mode 100644 index 000000000..023644192 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/sandbox.proto @@ -0,0 +1,53 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.types; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +// Sandbox represents a sandbox metadata object that keeps all info required by controller to +// work with a particular instance. +message Sandbox { + // SandboxID is a unique instance identifier within namespace + string sandbox_id = 1; + message Runtime { + // Name is the name of the runtime. + string name = 1; + // Options specify additional runtime initialization options for the shim (this data will be available in StartShim). + // Typically this data expected to be runtime shim implementation specific. + google.protobuf.Any options = 2; + } + // Runtime specifies which runtime to use for executing this container. + Runtime runtime = 2; + // Spec is sandbox configuration (kin of OCI runtime spec), spec's data will be written to a config.json file in the + // bundle directory (similary to OCI spec). + google.protobuf.Any spec = 3; + // Labels provides an area to include arbitrary data on containers. + map labels = 4; + // CreatedAt is the time the container was first created. + google.protobuf.Timestamp created_at = 5; + // UpdatedAt is the last time the container was mutated. + google.protobuf.Timestamp updated_at = 6; + // Extensions allow clients to provide optional blobs that can be handled by runtime. + map extensions = 7; + // Sandboxer is the name of the sandbox controller who manages the sandbox. + string sandboxer = 10; +} diff --git a/vendor/github.com/containerd/containerd/api/types/task/doc.go b/vendor/github.com/containerd/containerd/api/types/task/doc.go new file mode 100644 index 000000000..e10c7a469 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/task/doc.go @@ -0,0 +1,18 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package task defines the task service. +package task diff --git a/vendor/github.com/containerd/containerd/api/types/task/task.pb.go b/vendor/github.com/containerd/containerd/api/types/task/task.pb.go new file mode 100644 index 000000000..c81d4b7d3 --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/task/task.pb.go @@ -0,0 +1,404 @@ +// +//Copyright The containerd Authors. +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: types/task/task.proto + +package task + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Status int32 + +const ( + Status_UNKNOWN Status = 0 + Status_CREATED Status = 1 + Status_RUNNING Status = 2 + Status_STOPPED Status = 3 + Status_PAUSED Status = 4 + Status_PAUSING Status = 5 +) + +// Enum value maps for Status. +var ( + Status_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CREATED", + 2: "RUNNING", + 3: "STOPPED", + 4: "PAUSED", + 5: "PAUSING", + } + Status_value = map[string]int32{ + "UNKNOWN": 0, + "CREATED": 1, + "RUNNING": 2, + "STOPPED": 3, + "PAUSED": 4, + "PAUSING": 5, + } +) + +func (x Status) Enum() *Status { + p := new(Status) + *p = x + return p +} + +func (x Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Status) Descriptor() protoreflect.EnumDescriptor { + return file_types_task_task_proto_enumTypes[0].Descriptor() +} + +func (Status) Type() protoreflect.EnumType { + return &file_types_task_task_proto_enumTypes[0] +} + +func (x Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Status.Descriptor instead. +func (Status) EnumDescriptor() ([]byte, []int) { + return file_types_task_task_proto_rawDescGZIP(), []int{0} +} + +type Process struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"` + Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"` + ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,proto3" json:"exited_at,omitempty"` +} + +func (x *Process) Reset() { + *x = Process{} + if protoimpl.UnsafeEnabled { + mi := &file_types_task_task_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Process) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Process) ProtoMessage() {} + +func (x *Process) ProtoReflect() protoreflect.Message { + mi := &file_types_task_task_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Process.ProtoReflect.Descriptor instead. +func (*Process) Descriptor() ([]byte, []int) { + return file_types_task_task_proto_rawDescGZIP(), []int{0} +} + +func (x *Process) GetContainerID() string { + if x != nil { + return x.ContainerID + } + return "" +} + +func (x *Process) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *Process) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *Process) GetStatus() Status { + if x != nil { + return x.Status + } + return Status_UNKNOWN +} + +func (x *Process) GetStdin() string { + if x != nil { + return x.Stdin + } + return "" +} + +func (x *Process) GetStdout() string { + if x != nil { + return x.Stdout + } + return "" +} + +func (x *Process) GetStderr() string { + if x != nil { + return x.Stderr + } + return "" +} + +func (x *Process) GetTerminal() bool { + if x != nil { + return x.Terminal + } + return false +} + +func (x *Process) GetExitStatus() uint32 { + if x != nil { + return x.ExitStatus + } + return 0 +} + +func (x *Process) GetExitedAt() *timestamppb.Timestamp { + if x != nil { + return x.ExitedAt + } + return nil +} + +type ProcessInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // PID is the process ID. + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + // Info contains additional process information. + // + // Info varies by platform. + Info *anypb.Any `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"` +} + +func (x *ProcessInfo) Reset() { + *x = ProcessInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_types_task_task_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProcessInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessInfo) ProtoMessage() {} + +func (x *ProcessInfo) ProtoReflect() protoreflect.Message { + mi := &file_types_task_task_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessInfo.ProtoReflect.Descriptor instead. +func (*ProcessInfo) Descriptor() ([]byte, []int) { + return file_types_task_task_proto_rawDescGZIP(), []int{1} +} + +func (x *ProcessInfo) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *ProcessInfo) GetInfo() *anypb.Any { + if x != nil { + return x.Info + } + return nil +} + +var File_types_task_task_proto protoreflect.FileDescriptor + +var file_types_task_task_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x2f, 0x74, 0x61, 0x73, + 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x19, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x02, 0x0a, 0x07, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x74, 0x64, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x74, 0x64, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, + 0x64, 0x65, 0x72, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, + 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x65, 0x78, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x37, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x49, 0x0a, 0x0b, 0x50, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, + 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x2a, 0x55, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, + 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x55, 0x4e, + 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, + 0x44, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44, 0x10, 0x04, 0x12, + 0x0b, 0x0a, 0x07, 0x50, 0x41, 0x55, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x42, 0x31, 0x5a, 0x2f, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_types_task_task_proto_rawDescOnce sync.Once + file_types_task_task_proto_rawDescData = file_types_task_task_proto_rawDesc +) + +func file_types_task_task_proto_rawDescGZIP() []byte { + file_types_task_task_proto_rawDescOnce.Do(func() { + file_types_task_task_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_task_task_proto_rawDescData) + }) + return file_types_task_task_proto_rawDescData +} + +var file_types_task_task_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_types_task_task_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_types_task_task_proto_goTypes = []interface{}{ + (Status)(0), // 0: containerd.v1.types.Status + (*Process)(nil), // 1: containerd.v1.types.Process + (*ProcessInfo)(nil), // 2: containerd.v1.types.ProcessInfo + (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp + (*anypb.Any)(nil), // 4: google.protobuf.Any +} +var file_types_task_task_proto_depIdxs = []int32{ + 0, // 0: containerd.v1.types.Process.status:type_name -> containerd.v1.types.Status + 3, // 1: containerd.v1.types.Process.exited_at:type_name -> google.protobuf.Timestamp + 4, // 2: containerd.v1.types.ProcessInfo.info:type_name -> google.protobuf.Any + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_types_task_task_proto_init() } +func file_types_task_task_proto_init() { + if File_types_task_task_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_types_task_task_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Process); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_types_task_task_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProcessInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_task_task_proto_rawDesc, + NumEnums: 1, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_task_task_proto_goTypes, + DependencyIndexes: file_types_task_task_proto_depIdxs, + EnumInfos: file_types_task_task_proto_enumTypes, + MessageInfos: file_types_task_task_proto_msgTypes, + }.Build() + File_types_task_task_proto = out.File + file_types_task_task_proto_rawDesc = nil + file_types_task_task_proto_goTypes = nil + file_types_task_task_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/containerd/api/types/task/task.proto b/vendor/github.com/containerd/containerd/api/types/task/task.proto new file mode 100644 index 000000000..3a06236fa --- /dev/null +++ b/vendor/github.com/containerd/containerd/api/types/task/task.proto @@ -0,0 +1,55 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +syntax = "proto3"; + +package containerd.v1.types; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/containerd/containerd/api/types/task"; + +enum Status { + UNKNOWN = 0; + CREATED = 1; + RUNNING = 2; + STOPPED = 3; + PAUSED = 4; + PAUSING = 5; +} + +message Process { + string container_id = 1; + string id = 2; + uint32 pid = 3; + Status status = 4; + string stdin = 5; + string stdout = 6; + string stderr = 7; + bool terminal = 8; + uint32 exit_status = 9; + google.protobuf.Timestamp exited_at = 10; +} + +message ProcessInfo { + // PID is the process ID. + uint32 pid = 1; + // Info contains additional process information. + // + // Info varies by platform. + google.protobuf.Any info = 2; +} diff --git a/vendor/github.com/containerd/log/.golangci.yml b/vendor/github.com/containerd/log/.golangci.yml new file mode 100644 index 000000000..a695775df --- /dev/null +++ b/vendor/github.com/containerd/log/.golangci.yml @@ -0,0 +1,30 @@ +linters: + enable: + - exportloopref # Checks for pointers to enclosing loop variables + - gofmt + - goimports + - gosec + - ineffassign + - misspell + - nolintlint + - revive + - staticcheck + - tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17 + - unconvert + - unused + - vet + - dupword # Checks for duplicate words in the source code + disable: + - errcheck + +run: + timeout: 5m + skip-dirs: + - api + - cluster + - design + - docs + - docs/man + - releases + - reports + - test # e2e scripts diff --git a/vendor/github.com/containerd/log/LICENSE b/vendor/github.com/containerd/log/LICENSE new file mode 100644 index 000000000..584149b6e --- /dev/null +++ b/vendor/github.com/containerd/log/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright The containerd Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containerd/log/README.md b/vendor/github.com/containerd/log/README.md new file mode 100644 index 000000000..00e084988 --- /dev/null +++ b/vendor/github.com/containerd/log/README.md @@ -0,0 +1,17 @@ +# log + +A Go package providing a common logging interface across containerd repositories and a way for clients to use and configure logging in containerd packages. + +This package is not intended to be used as a standalone logging package outside of the containerd ecosystem and is intended as an interface wrapper around a logging implementation. +In the future this package may be replaced with a common go logging interface. + +## Project details + +**log** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. + diff --git a/vendor/github.com/containerd/log/context.go b/vendor/github.com/containerd/log/context.go new file mode 100644 index 000000000..20153066f --- /dev/null +++ b/vendor/github.com/containerd/log/context.go @@ -0,0 +1,182 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package log provides types and functions related to logging, passing +// loggers through a context, and attaching context to the logger. +// +// # Transitional types +// +// This package contains various types that are aliases for types in [logrus]. +// These aliases are intended for transitioning away from hard-coding logrus +// as logging implementation. Consumers of this package are encouraged to use +// the type-aliases from this package instead of directly using their logrus +// equivalent. +// +// The intent is to replace these aliases with locally defined types and +// interfaces once all consumers are no longer directly importing logrus +// types. +// +// IMPORTANT: due to the transitional purpose of this package, it is not +// guaranteed for the full logrus API to be provided in the future. As +// outlined, these aliases are provided as a step to transition away from +// a specific implementation which, as a result, exposes the full logrus API. +// While no decisions have been made on the ultimate design and interface +// provided by this package, we do not expect carrying "less common" features. +package log + +import ( + "context" + "fmt" + + "github.com/sirupsen/logrus" +) + +// G is a shorthand for [GetLogger]. +// +// We may want to define this locally to a package to get package tagged log +// messages. +var G = GetLogger + +// L is an alias for the standard logger. +var L = &Entry{ + Logger: logrus.StandardLogger(), + // Default is three fields plus a little extra room. + Data: make(Fields, 6), +} + +type loggerKey struct{} + +// Fields type to pass to "WithFields". +type Fields = map[string]any + +// Entry is a logging entry. It contains all the fields passed with +// [Entry.WithFields]. It's finally logged when Trace, Debug, Info, Warn, +// Error, Fatal or Panic is called on it. These objects can be reused and +// passed around as much as you wish to avoid field duplication. +// +// Entry is a transitional type, and currently an alias for [logrus.Entry]. +type Entry = logrus.Entry + +// RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using +// zeros to ensure the formatted time is always the same number of +// characters. +const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" + +// Level is a logging level. +type Level = logrus.Level + +// Supported log levels. +const ( + // TraceLevel level. Designates finer-grained informational events + // than [DebugLevel]. + TraceLevel Level = logrus.TraceLevel + + // DebugLevel level. Usually only enabled when debugging. Very verbose + // logging. + DebugLevel Level = logrus.DebugLevel + + // InfoLevel level. General operational entries about what's going on + // inside the application. + InfoLevel Level = logrus.InfoLevel + + // WarnLevel level. Non-critical entries that deserve eyes. + WarnLevel Level = logrus.WarnLevel + + // ErrorLevel level. Logs errors that should definitely be noted. + // Commonly used for hooks to send errors to an error tracking service. + ErrorLevel Level = logrus.ErrorLevel + + // FatalLevel level. Logs and then calls "logger.Exit(1)". It exits + // even if the logging level is set to Panic. + FatalLevel Level = logrus.FatalLevel + + // PanicLevel level. This is the highest level of severity. Logs and + // then calls panic with the message passed to Debug, Info, ... + PanicLevel Level = logrus.PanicLevel +) + +// SetLevel sets log level globally. It returns an error if the given +// level is not supported. +// +// level can be one of: +// +// - "trace" ([TraceLevel]) +// - "debug" ([DebugLevel]) +// - "info" ([InfoLevel]) +// - "warn" ([WarnLevel]) +// - "error" ([ErrorLevel]) +// - "fatal" ([FatalLevel]) +// - "panic" ([PanicLevel]) +func SetLevel(level string) error { + lvl, err := logrus.ParseLevel(level) + if err != nil { + return err + } + + L.Logger.SetLevel(lvl) + return nil +} + +// GetLevel returns the current log level. +func GetLevel() Level { + return L.Logger.GetLevel() +} + +// OutputFormat specifies a log output format. +type OutputFormat string + +// Supported log output formats. +const ( + // TextFormat represents the text logging format. + TextFormat OutputFormat = "text" + + // JSONFormat represents the JSON logging format. + JSONFormat OutputFormat = "json" +) + +// SetFormat sets the log output format ([TextFormat] or [JSONFormat]). +func SetFormat(format OutputFormat) error { + switch format { + case TextFormat: + L.Logger.SetFormatter(&logrus.TextFormatter{ + TimestampFormat: RFC3339NanoFixed, + FullTimestamp: true, + }) + return nil + case JSONFormat: + L.Logger.SetFormatter(&logrus.JSONFormatter{ + TimestampFormat: RFC3339NanoFixed, + }) + return nil + default: + return fmt.Errorf("unknown log format: %s", format) + } +} + +// WithLogger returns a new context with the provided logger. Use in +// combination with logger.WithField(s) for great effect. +func WithLogger(ctx context.Context, logger *Entry) context.Context { + return context.WithValue(ctx, loggerKey{}, logger.WithContext(ctx)) +} + +// GetLogger retrieves the current logger from the context. If no logger is +// available, the default logger is returned. +func GetLogger(ctx context.Context) *Entry { + if logger := ctx.Value(loggerKey{}); logger != nil { + return logger.(*Entry) + } + return L.WithContext(ctx) +} diff --git a/vendor/github.com/containerd/ttrpc/.gitattributes b/vendor/github.com/containerd/ttrpc/.gitattributes new file mode 100644 index 000000000..d207b1802 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/.gitattributes @@ -0,0 +1 @@ +*.go text eol=lf diff --git a/vendor/github.com/containerd/ttrpc/.gitignore b/vendor/github.com/containerd/ttrpc/.gitignore new file mode 100644 index 000000000..88ceb2764 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/.gitignore @@ -0,0 +1,13 @@ +# Binaries for programs and plugins +/bin/ +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +coverage.txt diff --git a/vendor/github.com/containerd/ttrpc/.golangci.yml b/vendor/github.com/containerd/ttrpc/.golangci.yml new file mode 100644 index 000000000..ef1a7d963 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/.golangci.yml @@ -0,0 +1,56 @@ +version: "2" +linters: + enable: + - misspell + - revive + - unconvert + disable: + - errcheck + settings: + revive: + rules: + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: error-return + - name: error-strings + - name: error-naming + - name: exported + - name: if-return + - name: increment-decrement + - name: var-naming + arguments: + - - UID + - GID + - [] + - name: var-declaration + - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: indent-error-flow + - name: errorf + - name: empty-block + - name: superfluous-else + - name: unused-parameter + - name: unreachable-code + - name: redefines-builtin-id + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - example +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - example diff --git a/vendor/github.com/containerd/ttrpc/LICENSE b/vendor/github.com/containerd/ttrpc/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containerd/ttrpc/Makefile b/vendor/github.com/containerd/ttrpc/Makefile new file mode 100644 index 000000000..c3a497dca --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/Makefile @@ -0,0 +1,180 @@ +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Go command to use for build +GO ?= go +INSTALL ?= install + +# Root directory of the project (absolute path). +ROOTDIR=$(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +WHALE = "🇩" +ONI = "👹" + +# Project binaries. +COMMANDS=protoc-gen-go-ttrpc protoc-gen-gogottrpc + +ifdef BUILDTAGS + GO_BUILDTAGS = ${BUILDTAGS} +endif +GO_BUILDTAGS ?= +GO_TAGS=$(if $(GO_BUILDTAGS),-tags "$(strip $(GO_BUILDTAGS))",) + +# Project packages. +PACKAGES=$(shell $(GO) list ${GO_TAGS} ./... | grep -v /example) +TESTPACKAGES=$(shell $(GO) list ${GO_TAGS} ./... | grep -v /cmd | grep -v /integration | grep -v /example) +BINPACKAGES=$(addprefix ./cmd/,$(COMMANDS)) + +#Replaces ":" (*nix), ";" (windows) with newline for easy parsing +GOPATHS=$(shell echo ${GOPATH} | tr ":" "\n" | tr ";" "\n") + +TESTFLAGS_RACE= +GO_BUILD_FLAGS= +# See Golang issue re: '-trimpath': https://github.com/golang/go/issues/13809 +GO_GCFLAGS=$(shell \ + set -- ${GOPATHS}; \ + echo "-gcflags=-trimpath=$${1}/src"; \ + ) + +BINARIES=$(addprefix bin/,$(COMMANDS)) + +# Flags passed to `go test` +TESTFLAGS ?= $(TESTFLAGS_RACE) $(EXTRA_TESTFLAGS) +TESTFLAGS_PARALLEL ?= 8 + +# Use this to replace `go test` with, for instance, `gotestsum` +GOTEST ?= $(GO) test + +.PHONY: clean all AUTHORS build binaries test integration generate protos check-protos coverage ci check help install vendor install-protobuf install-protobuild +.DEFAULT: default + +# Forcibly set the default goal to all, in case an include above brought in a rule definition. +.DEFAULT_GOAL := all + +all: binaries + +check: proto-fmt ## run all linters + @echo "$(WHALE) $@" + GOGC=75 golangci-lint run + +ci: check binaries check-protos coverage # coverage-integration ## to be used by the CI + +AUTHORS: .mailmap .git/HEAD + git log --format='%aN <%aE>' | sort -fu > $@ + +generate: protos + @echo "$(WHALE) $@" + @PATH="${ROOTDIR}/bin:${PATH}" $(GO) generate -x ${PACKAGES} + +protos: bin/protoc-gen-gogottrpc bin/protoc-gen-go-ttrpc ## generate protobuf + @echo "$(WHALE) $@" + @(PATH="${ROOTDIR}/bin:${PATH}" protobuild --quiet ${PACKAGES}) + +check-protos: protos ## check if protobufs needs to be generated again + @echo "$(WHALE) $@" + @test -z "$$(git status --short | grep ".pb.go" | tee /dev/stderr)" || \ + ((git diff | cat) && \ + (echo "$(ONI) please run 'make protos' when making changes to proto files" && false)) + +check-api-descriptors: protos ## check that protobuf changes aren't present. + @echo "$(WHALE) $@" + @test -z "$$(git status --short | grep ".pb.txt" | tee /dev/stderr)" || \ + ((git diff $$(find . -name '*.pb.txt') | cat) && \ + (echo "$(ONI) please run 'make protos' when making changes to proto files and check-in the generated descriptor file changes" && false)) + +proto-fmt: ## check format of proto files + @echo "$(WHALE) $@" + @test -z "$$(find . -name '*.proto' -type f -exec grep -Hn -e "^ " {} \; | tee /dev/stderr)" || \ + (echo "$(ONI) please indent proto files with tabs only" && false) + @test -z "$$(find . -name '*.proto' -type f -exec grep -Hn "Meta meta = " {} \; | grep -v '(gogoproto.nullable) = false' | tee /dev/stderr)" || \ + (echo "$(ONI) meta fields in proto files must have option (gogoproto.nullable) = false" && false) + +build: ## build the go packages + @echo "$(WHALE) $@" + @$(GO) build ${DEBUG_GO_GCFLAGS} ${GO_GCFLAGS} ${GO_BUILD_FLAGS} ${EXTRA_FLAGS} ${PACKAGES} + +test: ## run tests, except integration tests and tests that require root + @echo "$(WHALE) $@" + @$(GOTEST) ${TESTFLAGS} ${TESTPACKAGES} + +integration: ## run integration tests + @echo "$(WHALE) $@" + @cd "${ROOTDIR}/integration" && $(GOTEST) -v ${TESTFLAGS} -parallel ${TESTFLAGS_PARALLEL} . + +benchmark: ## run benchmarks tests + @echo "$(WHALE) $@" + @$(GO) test ${TESTFLAGS} -bench . -run Benchmark + +FORCE: + +define BUILD_BINARY +@echo "$(WHALE) $@" +@$(GO) build ${DEBUG_GO_GCFLAGS} ${GO_GCFLAGS} ${GO_BUILD_FLAGS} -o $@ ${GO_TAGS} ./$< +endef + +# Build a binary from a cmd. +bin/%: cmd/% FORCE + $(call BUILD_BINARY) + +binaries: $(BINARIES) ## build binaries + @echo "$(WHALE) $@" + +clean: ## clean up binaries + @echo "$(WHALE) $@" + @rm -f $(BINARIES) + +install: ## install binaries + @echo "$(WHALE) $@ $(BINPACKAGES)" + @$(GO) install $(BINPACKAGES) + +install-protobuf: + @echo "$(WHALE) $@" + @script/install-protobuf + +install-protobuild: + @echo "$(WHALE) $@" + @$(GO) install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1 + @$(GO) install github.com/containerd/protobuild@14832ccc41429f5c4f81028e5af08aa233a219cf + +coverage: ## generate coverprofiles from the unit tests, except tests that require root + @echo "$(WHALE) $@" + @rm -f coverage.txt + @$(GO) test ${TESTFLAGS} ${TESTPACKAGES} 2> /dev/null + @( for pkg in ${PACKAGES}; do \ + $(GO) test ${TESTFLAGS} \ + -cover \ + -coverprofile=profile.out \ + -covermode=atomic $$pkg || exit; \ + if [ -f profile.out ]; then \ + cat profile.out >> coverage.txt; \ + rm profile.out; \ + fi; \ + done ) + +vendor: ## ensure all the go.mod/go.sum files are up-to-date + @echo "$(WHALE) $@" + @$(GO) mod tidy + @$(GO) mod verify + +verify-vendor: ## verify if all the go.mod/go.sum files are up-to-date + @echo "$(WHALE) $@" + @$(GO) mod tidy + @$(GO) mod verify + @test -z "$$(git status --short | grep "go.sum" | tee /dev/stderr)" || \ + ((git diff | cat) && \ + (echo "$(ONI) make sure to checkin changes after go mod tidy" && false)) + +help: ## this help + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort diff --git a/vendor/github.com/containerd/ttrpc/PROTOCOL.md b/vendor/github.com/containerd/ttrpc/PROTOCOL.md new file mode 100644 index 000000000..12b43f6bd --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/PROTOCOL.md @@ -0,0 +1,240 @@ +# Protocol Specification + +The ttrpc protocol is client/server protocol to support multiple request streams +over a single connection with lightweight framing. The client represents the +process which initiated the underlying connection and the server is the process +which accepted the connection. The protocol is currently defined as +asymmetrical, with clients sending requests and servers sending responses. Both +clients and servers are able to send stream data. The roles are also used in +determining the stream identifiers, with client initiated streams using odd +number identifiers and server initiated using even number. The protocol may be +extended in the future to support server initiated streams, that is not +supported in the latest version. + +## Purpose + +The ttrpc protocol is designed to be lightweight and optimized for low latency +and reliable connections between processes on the same host. The protocol does +not include features for handling unreliable connections such as handshakes, +resets, pings, or flow control. The protocol is designed to make low-overhead +implementations as simple as possible. It is not intended as a suitable +replacement for HTTP2/3 over the network. + +## Message Frame + +Each Message Frame consists of a 10-byte message header followed +by message data. The data length and stream ID are both big-endian +4-byte unsigned integers. The message type is an unsigned 1-byte +integer. The flags are also an unsigned 1-byte integer and +use is defined by the message type. + + +---------------------------------------------------------------+ + | Data Length (32) | + +---------------------------------------------------------------+ + | Stream ID (32) | + +---------------+-----------------------------------------------+ + | Msg Type (8) | + +---------------+ + | Flags (8) | + +---------------+-----------------------------------------------+ + | Data (*) | + +---------------------------------------------------------------+ + +The Data Length field represents the number of bytes in the Data field. The +total frame size will always be Data Length + 10 bytes. The maximum data length +is 4MB and any larger size should be rejected. Due to the maximum data size +being less than 16MB, the first frame byte should always be zero. This first +byte should be considered reserved for future use. + +The Stream ID must be odd for client initiated streams and even for server +initiated streams. Server initiated streams are not currently supported. + +## Mesage Types + +| Message Type | Name | Description | +|--------------|----------|----------------------------------| +| 0x01 | Request | Initiates stream | +| 0x02 | Response | Final stream data and terminates | +| 0x03 | Data | Stream data | + +### Request + +The request message is used to initiate stream and send along request data for +properly routing and handling the stream. The stream may indicate unary without +any inbound or outbound stream data with only a response is expected on the +stream. The request may also indicate the stream is still open for more data and +no response is expected until data is finished. If the remote indicates the +stream is closed, the request may be considered non-unary but without anymore +stream data sent. In the case of `remote closed`, the remote still expects to +receive a response or stream data. For compatibility with non streaming clients, +a request with empty flags indicates a unary request. + +#### Request Flags + +| Flag | Name | Description | +|------|-----------------|--------------------------------------------------| +| 0x01 | `remote closed` | Non-unary, but no more data expected from remote | +| 0x02 | `remote open` | Non-unary, remote is still sending data | + +### Response + +The response message is used to end a stream with data, an empty response, or +an error. A response message is the only expected message after a unary request. +A non-unary request does not require a response message if the server is sending +back stream data. A non-unary stream may return a single response message but no +other stream data may follow. + +#### Response Flags + +No response flags are defined at this time, flags should be empty. + +### Data + +The data message is used to send data on an already initialized stream. Either +client or server may send data. A data message is not allowed on a unary stream. +A data message should not be sent after indicating `remote closed` to the peer. +The last data message on a stream must set the `remote closed` flag. + +The `no data` flag is used to indicate that the data message does not include +any data. This is normally used with the `remote closed` flag to indicate the +stream is now closed without transmitting any data. Since ttrpc normally +transmits a single object per message, a zero length data message may be +interpreted as an empty object. For example, transmitting the number zero as a +protobuf message ends up with a data length of zero, but the message is still +considered data and should be processed. + +#### Data Flags + +| Flag | Name | Description | +|------|-----------------|-----------------------------------| +| 0x01 | `remote closed` | No more data expected from remote | +| 0x04 | `no data` | This message does not have data | + +## Streaming + +All ttrpc requests use streams to transfer data. Unary streams will only have +two messages sent per stream, a request from a client and a response from the +server. Non-unary streams, however, may send any numbers of messages from the +client and the server. This makes stream management more complicated than unary +streams since both client and server need to track additional state. To keep +this management as simple as possible, ttrpc minimizes the number of states and +uses two flags instead of control frames. Each stream has two states while a +stream is still alive: `local closed` and `remote closed`. Each peer considers +local and remote from their own perspective and sets flags from the other peer's +perspective. For example, if a client sends a data frame with the +`remote closed` flag, that is indicating that the client is now `local closed` +and the server will be `remote closed`. A unary operation does not need to send +these flags since each received message always indicates `remote closed`. Once a +peer is both `local closed` and `remote closed`, the stream is considered +finished and may be cleaned up. + +Due to the asymmetric nature of the current protocol, a client should +always be in the `local closed` state before `remote closed` and a server should +always be in the `remote closed` state before `local closed`. This happens +because the client is always initiating requests and a client always expects a +final response back from a server to indicate the initiated request has been +fulfilled. This may mean server sends a final empty response to finish a stream +even after it has already completed sending data before the client. + +### Unary State Diagram + + +--------+ +--------+ + | Client | | Server | + +---+----+ +----+---+ + | +---------+ | + local >---------------+ Request +--------------------> remote + closed | +---------+ | closed + | | + | +----------+ | + finished <--------------+ Response +--------------------< finished + | +----------+ | + | | + +### Non-Unary State Diagrams + +RC: `remote closed` flag +RO: `remote open` flag + + +--------+ +--------+ + | Client | | Server | + +---+----+ +----+---+ + | +--------------+ | + >-------------+ Request [RO] +-----------------> + | +--------------+ | + | | + | +------+ | + >-----------------+ Data +---------------------> + | +------+ | + | | + | +-----------+ | + local >---------------+ Data [RC] +------------------> remote + closed | +-----------+ | closed + | | + | +----------+ | + finished <--------------+ Response +--------------------< finished + | +----------+ | + | | + + +--------+ +--------+ + | Client | | Server | + +---+----+ +----+---+ + | +--------------+ | + local >-------------+ Request [RC] +-----------------> remote + closed | +--------------+ | closed + | | + | +------+ | + <-----------------+ Data +---------------------< + | +------+ | + | | + | +-----------+ | + finished <---------------+ Data [RC] +------------------< finished + | +-----------+ | + | | + + +--------+ +--------+ + | Client | | Server | + +---+----+ +----+---+ + | +--------------+ | + >-------------+ Request [RO] +-----------------> + | +--------------+ | + | | + | +------+ | + >-----------------+ Data +---------------------> + | +------+ | + | | + | +------+ | + <-----------------+ Data +---------------------< + | +------+ | + | | + | +------+ | + >-----------------+ Data +---------------------> + | +------+ | + | | + | +-----------+ | + local >---------------+ Data [RC] +------------------> remote + closed | +-----------+ | closed + | | + | +------+ | + <-----------------+ Data +---------------------< + | +------+ | + | | + | +-----------+ | + finished <---------------+ Data [RC] +------------------< finished + | +-----------+ | + | | + +## RPC + +While this protocol is defined primarily to support Remote Procedure Calls, the +protocol does not define the request and response types beyond the messages +defined in the protocol. The implementation provides a default protobuf +definition of request and response which may be used for cross language rpc. +All implementations should at least define a request type which support +routing by procedure name and a response type which supports call status. + +## Version History + +| Version | Features | +|---------|---------------------| +| 1.0 | Unary requests only | +| 1.2 | Streaming support | diff --git a/vendor/github.com/containerd/ttrpc/Protobuild.toml b/vendor/github.com/containerd/ttrpc/Protobuild.toml new file mode 100644 index 000000000..0f6ccbd1e --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/Protobuild.toml @@ -0,0 +1,28 @@ +version = "2" +generators = ["go"] + +# Control protoc include paths. Below are usually some good defaults, but feel +# free to try it without them if it works for your project. +[includes] + # Include paths that will be added before all others. Typically, you want to + # treat the root of the project as an include, but this may not be necessary. + before = ["."] + + # Paths that will be added untouched to the end of the includes. We use + # `/usr/local/include` to pickup the common install location of protobuf. + # This is the default. + after = ["/usr/local/include"] + +# This section maps protobuf imports to Go packages. These will become +# `-M` directives in the call to the go protobuf generator. +[packages] + "google/protobuf/any.proto" = "github.com/gogo/protobuf/types" + "proto/status.proto" = "google.golang.org/genproto/googleapis/rpc/status" + +[[overrides]] +# enable ttrpc and disable fieldpath and grpc for the shim +prefixes = ["github.com/containerd/ttrpc/integration/streaming"] +generators = ["go", "go-ttrpc"] + +[overrides.parameters.go-ttrpc] +prefix = "TTRPC" diff --git a/vendor/github.com/containerd/ttrpc/README.md b/vendor/github.com/containerd/ttrpc/README.md new file mode 100644 index 000000000..ce95f63be --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/README.md @@ -0,0 +1,59 @@ +# ttrpc + +[![Build Status](https://github.com/containerd/ttrpc/actions/workflows/ci.yml/badge.svg)](https://github.com/containerd/ttrpc/actions/workflows/ci.yml) + +GRPC for low-memory environments. + +The existing grpc-go project requires a lot of memory overhead for importing +packages and at runtime. While this is great for many services with low density +requirements, this can be a problem when running a large number of services on +a single machine or on a machine with a small amount of memory. + +Using the same GRPC definitions, this project reduces the binary size and +protocol overhead required. We do this by eliding the `net/http`, `net/http2` +and `grpc` package used by grpc replacing it with a lightweight framing +protocol. The result are smaller binaries that use less resident memory with +the same ease of use as GRPC. + +Please note that while this project supports generating either end of the +protocol, the generated service definitions will be incompatible with regular +GRPC services, as they do not speak the same protocol. + +# Protocol + +See the [protocol specification](./PROTOCOL.md). + +# Usage + +Create a gogo vanity binary (see +[`cmd/protoc-gen-gogottrpc/main.go`](cmd/protoc-gen-gogottrpc/main.go) for an +example with the ttrpc plugin enabled. + +It's recommended to use [`protobuild`](https://github.com/containerd/protobuild) +to build the protobufs for this project, but this will work with protoc +directly, if required. + +# Differences from GRPC + +- The protocol stack has been replaced with a lighter protocol that doesn't + require http, http2 and tls. +- The client and server interface are identical whereas in GRPC there is a + client and server interface that are different. +- The Go stdlib context package is used instead. + +# Status + +TODO: + +- [ ] Add testing under concurrent load to ensure +- [ ] Verify connection error handling + +# Project details + +ttrpc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/ttrpc/channel.go b/vendor/github.com/containerd/ttrpc/channel.go new file mode 100644 index 000000000..872261e6d --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/channel.go @@ -0,0 +1,182 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "bufio" + "encoding/binary" + "fmt" + "io" + "net" + "sync" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + messageHeaderLength = 10 + messageLengthMax = 4 << 20 +) + +type messageType uint8 + +const ( + messageTypeRequest messageType = 0x1 + messageTypeResponse messageType = 0x2 + messageTypeData messageType = 0x3 +) + +func (mt messageType) String() string { + switch mt { + case messageTypeRequest: + return "request" + case messageTypeResponse: + return "response" + case messageTypeData: + return "data" + default: + return "unknown" + } +} + +const ( + flagRemoteClosed uint8 = 0x1 + flagRemoteOpen uint8 = 0x2 + flagNoData uint8 = 0x4 +) + +// messageHeader represents the fixed-length message header of 10 bytes sent +// with every request. +type messageHeader struct { + Length uint32 // length excluding this header. b[:4] + StreamID uint32 // identifies which request stream message is a part of. b[4:8] + Type messageType // message type b[8] + Flags uint8 // type specific flags b[9] +} + +func readMessageHeader(p []byte, r io.Reader) (messageHeader, error) { + _, err := io.ReadFull(r, p[:messageHeaderLength]) + if err != nil { + return messageHeader{}, err + } + + return messageHeader{ + Length: binary.BigEndian.Uint32(p[:4]), + StreamID: binary.BigEndian.Uint32(p[4:8]), + Type: messageType(p[8]), + Flags: p[9], + }, nil +} + +func writeMessageHeader(w io.Writer, p []byte, mh messageHeader) error { + binary.BigEndian.PutUint32(p[:4], mh.Length) + binary.BigEndian.PutUint32(p[4:8], mh.StreamID) + p[8] = byte(mh.Type) + p[9] = mh.Flags + + _, err := w.Write(p[:]) + return err +} + +var buffers sync.Pool + +type channel struct { + conn net.Conn + bw *bufio.Writer + br *bufio.Reader + hrbuf [messageHeaderLength]byte // avoid alloc when reading header + hwbuf [messageHeaderLength]byte +} + +func newChannel(conn net.Conn) *channel { + return &channel{ + conn: conn, + bw: bufio.NewWriter(conn), + br: bufio.NewReader(conn), + } +} + +// recv a message from the channel. The returned buffer contains the message. +// +// If a valid grpc status is returned, the message header +// returned will be valid and caller should send that along to +// the correct consumer. The bytes on the underlying channel +// will be discarded. +func (ch *channel) recv() (messageHeader, []byte, error) { + mh, err := readMessageHeader(ch.hrbuf[:], ch.br) + if err != nil { + return messageHeader{}, nil, err + } + + if mh.Length > uint32(messageLengthMax) { + if _, err := ch.br.Discard(int(mh.Length)); err != nil { + return mh, nil, fmt.Errorf("failed to discard after receiving oversized message: %w", err) + } + + return mh, nil, status.Errorf(codes.ResourceExhausted, "message length %v exceed maximum message size of %v", mh.Length, messageLengthMax) + } + + var p []byte + if mh.Length > 0 { + p = ch.getmbuf(int(mh.Length)) + if _, err := io.ReadFull(ch.br, p); err != nil { + return messageHeader{}, nil, fmt.Errorf("failed reading message: %w", err) + } + } + + return mh, p, nil +} + +func (ch *channel) send(streamID uint32, t messageType, flags uint8, p []byte) error { + if len(p) > messageLengthMax { + return OversizedMessageError(len(p)) + } + + if err := writeMessageHeader(ch.bw, ch.hwbuf[:], messageHeader{Length: uint32(len(p)), StreamID: streamID, Type: t, Flags: flags}); err != nil { + return err + } + + if len(p) > 0 { + _, err := ch.bw.Write(p) + if err != nil { + return err + } + } + + return ch.bw.Flush() +} + +func (ch *channel) getmbuf(size int) []byte { + // we can't use the standard New method on pool because we want to allocate + // based on size. + b, ok := buffers.Get().(*[]byte) + if !ok || cap(*b) < size { + // TODO(stevvooe): It may be better to allocate these in fixed length + // buckets to reduce fragmentation but its not clear that would help + // with performance. An ilogb approach or similar would work well. + bb := make([]byte, size) + b = &bb + } else { + *b = (*b)[:size] + } + return *b +} + +func (ch *channel) putmbuf(p []byte) { + buffers.Put(&p) +} diff --git a/vendor/github.com/containerd/ttrpc/client.go b/vendor/github.com/containerd/ttrpc/client.go new file mode 100644 index 000000000..be20ed489 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/client.go @@ -0,0 +1,571 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "strings" + "sync" + "syscall" + "time" + + "github.com/containerd/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Client for a ttrpc server +type Client struct { + codec codec + conn net.Conn + channel *channel + + streamLock sync.RWMutex + streams map[streamID]*stream + nextStreamID streamID + sendLock sync.Mutex + + ctx context.Context + closed func() + + closeOnce sync.Once + userCloseFunc func() + userCloseWaitCh chan struct{} + + interceptor UnaryClientInterceptor +} + +// ClientOpts configures a client +type ClientOpts func(c *Client) + +// WithOnClose sets the close func whenever the client's Close() method is called +func WithOnClose(onClose func()) ClientOpts { + return func(c *Client) { + c.userCloseFunc = onClose + } +} + +// WithUnaryClientInterceptor sets the provided client interceptor +func WithUnaryClientInterceptor(i UnaryClientInterceptor) ClientOpts { + return func(c *Client) { + c.interceptor = i + } +} + +// WithChainUnaryClientInterceptor sets the provided chain of client interceptors +func WithChainUnaryClientInterceptor(interceptors ...UnaryClientInterceptor) ClientOpts { + return func(c *Client) { + if len(interceptors) == 0 { + return + } + if c.interceptor != nil { + interceptors = append([]UnaryClientInterceptor{c.interceptor}, interceptors...) + } + c.interceptor = func( + ctx context.Context, + req *Request, + reply *Response, + info *UnaryClientInfo, + final Invoker, + ) error { + return interceptors[0](ctx, req, reply, info, + chainUnaryInterceptors(interceptors[1:], final, info)) + } + } +} + +func chainUnaryInterceptors(interceptors []UnaryClientInterceptor, final Invoker, info *UnaryClientInfo) Invoker { + if len(interceptors) == 0 { + return final + } + return func( + ctx context.Context, + req *Request, + reply *Response, + ) error { + return interceptors[0](ctx, req, reply, info, + chainUnaryInterceptors(interceptors[1:], final, info)) + } +} + +// NewClient creates a new ttrpc client using the given connection +func NewClient(conn net.Conn, opts ...ClientOpts) *Client { + ctx, cancel := context.WithCancel(context.Background()) + channel := newChannel(conn) + c := &Client{ + codec: codec{}, + conn: conn, + channel: channel, + streams: make(map[streamID]*stream), + nextStreamID: 1, + closed: cancel, + ctx: ctx, + userCloseFunc: func() {}, + userCloseWaitCh: make(chan struct{}), + } + + for _, o := range opts { + o(c) + } + + if c.interceptor == nil { + c.interceptor = defaultClientInterceptor + } + + go c.run() + return c +} + +func (c *Client) send(sid uint32, mt messageType, flags uint8, b []byte) error { + c.sendLock.Lock() + defer c.sendLock.Unlock() + return c.channel.send(sid, mt, flags, b) +} + +// Call makes a unary request and returns with response +func (c *Client) Call(ctx context.Context, service, method string, req, resp interface{}) error { + payload, err := c.codec.Marshal(req) + if err != nil { + return err + } + + var ( + creq = &Request{ + Service: service, + Method: method, + Payload: payload, + // TODO: metadata from context + } + + cresp = &Response{} + ) + + if metadata, ok := GetMetadata(ctx); ok { + metadata.setRequest(creq) + } + + if dl, ok := ctx.Deadline(); ok { + creq.TimeoutNano = time.Until(dl).Nanoseconds() + } + + info := &UnaryClientInfo{ + FullMethod: fullPath(service, method), + } + if err := c.interceptor(ctx, creq, cresp, info, c.dispatch); err != nil { + return err + } + + if err := c.codec.Unmarshal(cresp.Payload, resp); err != nil { + return err + } + + if cresp.Status != nil && cresp.Status.Code != int32(codes.OK) { + return status.ErrorProto(cresp.Status) + } + return nil +} + +// StreamDesc describes the stream properties, whether the stream has +// a streaming client, a streaming server, or both +type StreamDesc struct { + StreamingClient bool + StreamingServer bool +} + +// ClientStream is used to send or recv messages on the underlying stream +type ClientStream interface { + CloseSend() error + SendMsg(m interface{}) error + RecvMsg(m interface{}) error +} + +type clientStream struct { + ctx context.Context + s *stream + c *Client + desc *StreamDesc + localClosed bool + remoteClosed bool +} + +func (cs *clientStream) CloseSend() error { + if !cs.desc.StreamingClient { + return fmt.Errorf("%w: cannot close non-streaming client", ErrProtocol) + } + if cs.localClosed { + return ErrStreamClosed + } + err := cs.s.send(messageTypeData, flagRemoteClosed|flagNoData, nil) + if err != nil { + return filterCloseErr(err) + } + cs.localClosed = true + return nil +} + +func (cs *clientStream) SendMsg(m interface{}) error { + if !cs.desc.StreamingClient { + return fmt.Errorf("%w: cannot send data from non-streaming client", ErrProtocol) + } + if cs.localClosed { + return ErrStreamClosed + } + + var ( + payload []byte + err error + ) + if m != nil { + payload, err = cs.c.codec.Marshal(m) + if err != nil { + return err + } + } + + err = cs.s.send(messageTypeData, 0, payload) + if err != nil { + return filterCloseErr(err) + } + + return nil +} + +func (cs *clientStream) RecvMsg(m interface{}) error { + if cs.remoteClosed { + return io.EOF + } + + var msg *streamMessage + select { + case <-cs.ctx.Done(): + return cs.ctx.Err() + case <-cs.s.recvClose: + // If recv has a pending message, process that first + select { + case msg = <-cs.s.recv: + default: + return cs.s.recvErr + } + case msg = <-cs.s.recv: + } + + switch msg.header.Type { + case messageTypeResponse: + resp := &Response{} + err := proto.Unmarshal(msg.payload[:msg.header.Length], resp) + // return the payload buffer for reuse + cs.c.channel.putmbuf(msg.payload) + if err != nil { + return err + } + + if err := cs.c.codec.Unmarshal(resp.Payload, m); err != nil { + return err + } + + if resp.Status != nil && resp.Status.Code != int32(codes.OK) { + return status.ErrorProto(resp.Status) + } + + cs.c.deleteStream(cs.s) + cs.remoteClosed = true + + return nil + case messageTypeData: + if !cs.desc.StreamingServer { + cs.c.deleteStream(cs.s) + cs.remoteClosed = true + return fmt.Errorf("received data from non-streaming server: %w", ErrProtocol) + } + if msg.header.Flags&flagRemoteClosed == flagRemoteClosed { + cs.c.deleteStream(cs.s) + cs.remoteClosed = true + + if msg.header.Flags&flagNoData == flagNoData { + return io.EOF + } + } + + err := cs.c.codec.Unmarshal(msg.payload[:msg.header.Length], m) + cs.c.channel.putmbuf(msg.payload) + if err != nil { + return err + } + return nil + default: + return fmt.Errorf("unexpected %q message received: %w", msg.header.Type, ErrProtocol) + } +} + +// Close closes the ttrpc connection and underlying connection +func (c *Client) Close() error { + c.closeOnce.Do(func() { + c.closed() + + c.conn.Close() + }) + return nil +} + +// UserOnCloseWait is used to block until the user's on-close callback +// finishes. +func (c *Client) UserOnCloseWait(ctx context.Context) error { + select { + case <-c.userCloseWaitCh: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} + +func (c *Client) run() { + err := c.receiveLoop() + c.Close() + c.cleanupStreams(err) + + c.userCloseFunc() + close(c.userCloseWaitCh) +} + +func (c *Client) receiveLoop() error { + for { + select { + case <-c.ctx.Done(): + return ErrClosed + default: + var ( + msg = &streamMessage{} + err error + ) + + msg.header, msg.payload, err = c.channel.recv() + if err != nil { + _, ok := status.FromError(err) + if !ok { + // treat all errors that are not an rpc status as terminal. + // all others poison the connection. + return filterCloseErr(err) + } + } + sid := streamID(msg.header.StreamID) + s := c.getStream(sid) + if s == nil { + log.G(c.ctx).WithField("stream", sid).Error("ttrpc: received message on inactive stream") + continue + } + + if err != nil { + s.closeWithError(err) + } else { + if err := s.receive(c.ctx, msg); err != nil { + log.G(c.ctx).WithFields(log.Fields{"error": err, "stream": sid}).Error("ttrpc: failed to handle message") + } + } + } + } +} + +// createStream creates a new stream and registers it with the client +// Introduce stream types for multiple or single response +func (c *Client) createStream(flags uint8, b []byte) (*stream, error) { + // sendLock must be held across both allocation of the stream ID and sending it across the wire. + // This ensures that new stream IDs sent on the wire are always increasing, which is a + // requirement of the TTRPC protocol. + // This use of sendLock could be split into another mutex that covers stream creation + first send, + // and just use sendLock to guard writing to the wire, but for now it seems simpler to have fewer mutexes. + c.sendLock.Lock() + defer c.sendLock.Unlock() + + // Check if closed since lock acquired to prevent adding + // anything after cleanup completes + select { + case <-c.ctx.Done(): + return nil, ErrClosed + default: + } + + var s *stream + if err := func() error { + // In the future this could be replaced with a sync.Map instead of streamLock+map. + c.streamLock.Lock() + defer c.streamLock.Unlock() + + // Check if closed since lock acquired to prevent adding + // anything after cleanup completes + select { + case <-c.ctx.Done(): + return ErrClosed + default: + } + + s = newStream(c.nextStreamID, c) + c.streams[s.id] = s + c.nextStreamID = c.nextStreamID + 2 + + return nil + }(); err != nil { + return nil, err + } + + if err := c.channel.send(uint32(s.id), messageTypeRequest, flags, b); err != nil { + return s, filterCloseErr(err) + } + + return s, nil +} + +func (c *Client) deleteStream(s *stream) { + c.streamLock.Lock() + delete(c.streams, s.id) + c.streamLock.Unlock() + s.closeWithError(nil) +} + +func (c *Client) getStream(sid streamID) *stream { + c.streamLock.RLock() + s := c.streams[sid] + c.streamLock.RUnlock() + return s +} + +func (c *Client) cleanupStreams(err error) { + c.streamLock.Lock() + defer c.streamLock.Unlock() + + for sid, s := range c.streams { + s.closeWithError(err) + delete(c.streams, sid) + } +} + +// filterCloseErr rewrites EOF and EPIPE errors to ErrClosed. Use when +// returning from call or handling errors from main read loop. +// +// This purposely ignores errors with a wrapped cause. +func filterCloseErr(err error) error { + switch { + case err == nil: + return nil + case err == io.EOF: + return ErrClosed + case errors.Is(err, io.ErrClosedPipe): + return ErrClosed + case errors.Is(err, io.EOF): + return ErrClosed + case strings.Contains(err.Error(), "use of closed network connection"): + return ErrClosed + default: + // if we have an epipe on a write or econnreset on a read , we cast to errclosed + var oerr *net.OpError + if errors.As(err, &oerr) { + if (oerr.Op == "write" && errors.Is(err, syscall.EPIPE)) || + (oerr.Op == "read" && errors.Is(err, syscall.ECONNRESET)) { + return ErrClosed + } + } + } + + return err +} + +// NewStream creates a new stream with the given stream descriptor to the +// specified service and method. If not a streaming client, the request object +// may be provided. +func (c *Client) NewStream(ctx context.Context, desc *StreamDesc, service, method string, req interface{}) (ClientStream, error) { + var payload []byte + if req != nil { + var err error + payload, err = c.codec.Marshal(req) + if err != nil { + return nil, err + } + } + + request := &Request{ + Service: service, + Method: method, + Payload: payload, + // TODO: metadata from context + } + p, err := c.codec.Marshal(request) + if err != nil { + return nil, err + } + + var flags uint8 + if desc.StreamingClient { + flags = flagRemoteOpen + } else { + flags = flagRemoteClosed + } + s, err := c.createStream(flags, p) + if err != nil { + return nil, err + } + + return &clientStream{ + ctx: ctx, + s: s, + c: c, + desc: desc, + }, nil +} + +func (c *Client) dispatch(ctx context.Context, req *Request, resp *Response) error { + p, err := c.codec.Marshal(req) + if err != nil { + return err + } + + s, err := c.createStream(0, p) + if err != nil { + return err + } + defer c.deleteStream(s) + + var msg *streamMessage + select { + case <-ctx.Done(): + return ctx.Err() + case <-c.ctx.Done(): + return ErrClosed + case <-s.recvClose: + // If recv has a pending message, process that first + select { + case msg = <-s.recv: + default: + return s.recvErr + } + case msg = <-s.recv: + } + + if msg.header.Type == messageTypeResponse { + err = proto.Unmarshal(msg.payload[:msg.header.Length], resp) + } else { + err = fmt.Errorf("unexpected %q message received: %w", msg.header.Type, ErrProtocol) + } + + // return the payload buffer for reuse + c.channel.putmbuf(msg.payload) + + return err +} diff --git a/vendor/github.com/containerd/ttrpc/codec.go b/vendor/github.com/containerd/ttrpc/codec.go new file mode 100644 index 000000000..3e82722a4 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/codec.go @@ -0,0 +1,43 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "fmt" + + "google.golang.org/protobuf/proto" +) + +type codec struct{} + +func (c codec) Marshal(msg interface{}) ([]byte, error) { + switch v := msg.(type) { + case proto.Message: + return proto.Marshal(v) + default: + return nil, fmt.Errorf("ttrpc: cannot marshal unknown type: %T", msg) + } +} + +func (c codec) Unmarshal(p []byte, msg interface{}) error { + switch v := msg.(type) { + case proto.Message: + return proto.Unmarshal(p, v) + default: + return fmt.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg) + } +} diff --git a/vendor/github.com/containerd/ttrpc/config.go b/vendor/github.com/containerd/ttrpc/config.go new file mode 100644 index 000000000..f401f67be --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/config.go @@ -0,0 +1,86 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "context" + "errors" +) + +type serverConfig struct { + handshaker Handshaker + interceptor UnaryServerInterceptor +} + +// ServerOpt for configuring a ttrpc server +type ServerOpt func(*serverConfig) error + +// WithServerHandshaker can be passed to NewServer to ensure that the +// handshaker is called before every connection attempt. +// +// Only one handshaker is allowed per server. +func WithServerHandshaker(handshaker Handshaker) ServerOpt { + return func(c *serverConfig) error { + if c.handshaker != nil { + return errors.New("only one handshaker allowed per server") + } + c.handshaker = handshaker + return nil + } +} + +// WithUnaryServerInterceptor sets the provided interceptor on the server +func WithUnaryServerInterceptor(i UnaryServerInterceptor) ServerOpt { + return func(c *serverConfig) error { + if c.interceptor != nil { + return errors.New("only one unchained interceptor allowed per server") + } + c.interceptor = i + return nil + } +} + +// WithChainUnaryServerInterceptor sets the provided chain of server interceptors +func WithChainUnaryServerInterceptor(interceptors ...UnaryServerInterceptor) ServerOpt { + return func(c *serverConfig) error { + if len(interceptors) == 0 { + return nil + } + if c.interceptor != nil { + interceptors = append([]UnaryServerInterceptor{c.interceptor}, interceptors...) + } + c.interceptor = func( + ctx context.Context, + unmarshal Unmarshaler, + info *UnaryServerInfo, + method Method) (interface{}, error) { + return interceptors[0](ctx, unmarshal, info, + chainUnaryServerInterceptors(info, method, interceptors[1:])) + } + return nil + } +} + +func chainUnaryServerInterceptors(info *UnaryServerInfo, method Method, interceptors []UnaryServerInterceptor) Method { + if len(interceptors) == 0 { + return method + } + return func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + return interceptors[0](ctx, unmarshal, info, + chainUnaryServerInterceptors(info, method, interceptors[1:])) + } +} diff --git a/vendor/github.com/containerd/ttrpc/doc.go b/vendor/github.com/containerd/ttrpc/doc.go new file mode 100644 index 000000000..d80cd424c --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/doc.go @@ -0,0 +1,23 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* +package ttrpc defines and implements a low level simple transfer protocol +optimized for low latency and reliable connections between processes on the same +host. The protocol uses simple framing for sending requests, responses, and data +using multiple streams. +*/ +package ttrpc diff --git a/vendor/github.com/containerd/ttrpc/errors.go b/vendor/github.com/containerd/ttrpc/errors.go new file mode 100644 index 000000000..632dbe8bd --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/errors.go @@ -0,0 +1,80 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "errors" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var ( + // ErrProtocol is a general error in the handling the protocol. + ErrProtocol = errors.New("protocol error") + + // ErrClosed is returned by client methods when the underlying connection is + // closed. + ErrClosed = errors.New("ttrpc: closed") + + // ErrServerClosed is returned when the Server has closed its connection. + ErrServerClosed = errors.New("ttrpc: server closed") + + // ErrStreamClosed is when the streaming connection is closed. + ErrStreamClosed = errors.New("ttrpc: stream closed") +) + +// OversizedMessageErr is used to indicate refusal to send an oversized message. +// It wraps a ResourceExhausted grpc Status together with the offending message +// length. +type OversizedMessageErr struct { + messageLength int + err error +} + +// OversizedMessageError returns an OversizedMessageErr error for the given message +// length if it exceeds the allowed maximum. Otherwise a nil error is returned. +func OversizedMessageError(messageLength int) error { + if messageLength <= messageLengthMax { + return nil + } + + return &OversizedMessageErr{ + messageLength: messageLength, + err: status.Errorf(codes.ResourceExhausted, "message length %v exceed maximum message size of %v", messageLength, messageLengthMax), + } +} + +// Error returns the error message for the corresponding grpc Status for the error. +func (e *OversizedMessageErr) Error() string { + return e.err.Error() +} + +// Unwrap returns the corresponding error with our grpc status code. +func (e *OversizedMessageErr) Unwrap() error { + return e.err +} + +// RejectedLength retrieves the rejected message length which triggered the error. +func (e *OversizedMessageErr) RejectedLength() int { + return e.messageLength +} + +// MaximumLength retrieves the maximum allowed message length that triggered the error. +func (*OversizedMessageErr) MaximumLength() int { + return messageLengthMax +} diff --git a/vendor/github.com/containerd/ttrpc/handshake.go b/vendor/github.com/containerd/ttrpc/handshake.go new file mode 100644 index 000000000..3c6b610d3 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/handshake.go @@ -0,0 +1,50 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "context" + "net" +) + +// Handshaker defines the interface for connection handshakes performed on the +// server or client when first connecting. +type Handshaker interface { + // Handshake should confirm or decorate a connection that may be incoming + // to a server or outgoing from a client. + // + // If this returns without an error, the caller should use the connection + // in place of the original connection. + // + // The second return value can contain credential specific data, such as + // unix socket credentials or TLS information. + // + // While we currently only have implementations on the server-side, this + // interface should be sufficient to implement similar handshakes on the + // client-side. + Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) +} + +type handshakerFunc func(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) + +func (fn handshakerFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) { + return fn(ctx, conn) +} + +func noopHandshake(_ context.Context, conn net.Conn) (net.Conn, interface{}, error) { + return conn, nil, nil +} diff --git a/vendor/github.com/containerd/ttrpc/interceptor.go b/vendor/github.com/containerd/ttrpc/interceptor.go new file mode 100644 index 000000000..7ff5e9d33 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/interceptor.go @@ -0,0 +1,65 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import "context" + +// UnaryServerInfo provides information about the server request +type UnaryServerInfo struct { + FullMethod string +} + +// UnaryClientInfo provides information about the client request +type UnaryClientInfo struct { + FullMethod string +} + +// StreamServerInfo provides information about the server request +type StreamServerInfo struct { + FullMethod string + StreamingClient bool + StreamingServer bool +} + +// Unmarshaler contains the server request data and allows it to be unmarshaled +// into a concrete type +type Unmarshaler func(interface{}) error + +// Invoker invokes the client's request and response from the ttrpc server +type Invoker func(context.Context, *Request, *Response) error + +// UnaryServerInterceptor specifies the interceptor function for server request/response +type UnaryServerInterceptor func(context.Context, Unmarshaler, *UnaryServerInfo, Method) (interface{}, error) + +// UnaryClientInterceptor specifies the interceptor function for client request/response +type UnaryClientInterceptor func(context.Context, *Request, *Response, *UnaryClientInfo, Invoker) error + +func defaultServerInterceptor(ctx context.Context, unmarshal Unmarshaler, _ *UnaryServerInfo, method Method) (interface{}, error) { + return method(ctx, unmarshal) +} + +func defaultClientInterceptor(ctx context.Context, req *Request, resp *Response, _ *UnaryClientInfo, invoker Invoker) error { + return invoker(ctx, req, resp) +} + +type StreamServerInterceptor func(context.Context, StreamServer, *StreamServerInfo, StreamHandler) (interface{}, error) + +func defaultStreamServerInterceptor(ctx context.Context, ss StreamServer, _ *StreamServerInfo, stream StreamHandler) (interface{}, error) { + return stream(ctx, ss) +} + +type StreamClientInterceptor func(context.Context) diff --git a/vendor/github.com/containerd/ttrpc/metadata.go b/vendor/github.com/containerd/ttrpc/metadata.go new file mode 100644 index 000000000..6e0042487 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/metadata.go @@ -0,0 +1,135 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "context" + "strings" +) + +// MD is the user type for ttrpc metadata +type MD map[string][]string + +// Get returns the metadata for a given key when they exist. +// If there is no metadata, a nil slice and false are returned. +func (m MD) Get(key string) ([]string, bool) { + key = strings.ToLower(key) + list, ok := m[key] + if !ok || len(list) == 0 { + return nil, false + } + + return list, true +} + +// Set sets the provided values for a given key. +// The values will overwrite any existing values. +// If no values provided, a key will be deleted. +func (m MD) Set(key string, values ...string) { + key = strings.ToLower(key) + if len(values) == 0 { + delete(m, key) + return + } + m[key] = values +} + +// Append appends additional values to the given key. +func (m MD) Append(key string, values ...string) { + key = strings.ToLower(key) + if len(values) == 0 { + return + } + current, ok := m[key] + if ok { + m.Set(key, append(current, values...)...) + } else { + m.Set(key, values...) + } +} + +// Clone returns a copy of MD or nil if it's nil. +// It's copied from golang's `http.Header.Clone` implementation: +// https://cs.opensource.google/go/go/+/refs/tags/go1.23.4:src/net/http/header.go;l=94 +func (m MD) Clone() MD { + if m == nil { + return nil + } + + // Find total number of values. + nv := 0 + for _, vv := range m { + nv += len(vv) + } + sv := make([]string, nv) // shared backing array for headers' values + m2 := make(MD, len(m)) + for k, vv := range m { + if vv == nil { + // Preserve nil values. + m2[k] = nil + continue + } + n := copy(sv, vv) + m2[k] = sv[:n:n] + sv = sv[n:] + } + return m2 +} + +func (m MD) setRequest(r *Request) { + for k, values := range m { + for _, v := range values { + r.Metadata = append(r.Metadata, &KeyValue{ + Key: k, + Value: v, + }) + } + } +} + +func (m MD) fromRequest(r *Request) { + for _, kv := range r.Metadata { + m[kv.Key] = append(m[kv.Key], kv.Value) + } +} + +type metadataKey struct{} + +// GetMetadata retrieves metadata from context.Context (previously attached with WithMetadata) +func GetMetadata(ctx context.Context) (MD, bool) { + metadata, ok := ctx.Value(metadataKey{}).(MD) + return metadata, ok +} + +// GetMetadataValue gets a specific metadata value by name from context.Context +func GetMetadataValue(ctx context.Context, name string) (string, bool) { + metadata, ok := GetMetadata(ctx) + if !ok { + return "", false + } + + if list, ok := metadata.Get(name); ok { + return list[0], true + } + + return "", false +} + +// WithMetadata attaches metadata map to a context.Context +func WithMetadata(ctx context.Context, md MD) context.Context { + return context.WithValue(ctx, metadataKey{}, md) +} diff --git a/vendor/github.com/containerd/ttrpc/request.pb.go b/vendor/github.com/containerd/ttrpc/request.pb.go new file mode 100644 index 000000000..3921ae5a3 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/request.pb.go @@ -0,0 +1,396 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.20.1 +// source: github.com/containerd/ttrpc/request.proto + +package ttrpc + +import ( + status "google.golang.org/genproto/googleapis/rpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` + Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + TimeoutNano int64 `protobuf:"varint,4,opt,name=timeout_nano,json=timeoutNano,proto3" json:"timeout_nano,omitempty"` + Metadata []*KeyValue `protobuf:"bytes,5,rep,name=metadata,proto3" json:"metadata,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_containerd_ttrpc_request_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Request) ProtoMessage() {} + +func (x *Request) ProtoReflect() protoreflect.Message { + mi := &file_github_com_containerd_ttrpc_request_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Request.ProtoReflect.Descriptor instead. +func (*Request) Descriptor() ([]byte, []int) { + return file_github_com_containerd_ttrpc_request_proto_rawDescGZIP(), []int{0} +} + +func (x *Request) GetService() string { + if x != nil { + return x.Service + } + return "" +} + +func (x *Request) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *Request) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +func (x *Request) GetTimeoutNano() int64 { + if x != nil { + return x.TimeoutNano + } + return 0 +} + +func (x *Request) GetMetadata() []*KeyValue { + if x != nil { + return x.Metadata + } + return nil +} + +type Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status *status.Status `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *Response) Reset() { + *x = Response{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_containerd_ttrpc_request_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Response) ProtoMessage() {} + +func (x *Response) ProtoReflect() protoreflect.Message { + mi := &file_github_com_containerd_ttrpc_request_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Response.ProtoReflect.Descriptor instead. +func (*Response) Descriptor() ([]byte, []int) { + return file_github_com_containerd_ttrpc_request_proto_rawDescGZIP(), []int{1} +} + +func (x *Response) GetStatus() *status.Status { + if x != nil { + return x.Status + } + return nil +} + +func (x *Response) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +type StringList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + List []string `protobuf:"bytes,1,rep,name=list,proto3" json:"list,omitempty"` +} + +func (x *StringList) Reset() { + *x = StringList{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_containerd_ttrpc_request_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringList) ProtoMessage() {} + +func (x *StringList) ProtoReflect() protoreflect.Message { + mi := &file_github_com_containerd_ttrpc_request_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringList.ProtoReflect.Descriptor instead. +func (*StringList) Descriptor() ([]byte, []int) { + return file_github_com_containerd_ttrpc_request_proto_rawDescGZIP(), []int{2} +} + +func (x *StringList) GetList() []string { + if x != nil { + return x.List + } + return nil +} + +type KeyValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *KeyValue) Reset() { + *x = KeyValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_containerd_ttrpc_request_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *KeyValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KeyValue) ProtoMessage() {} + +func (x *KeyValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_containerd_ttrpc_request_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KeyValue.ProtoReflect.Descriptor instead. +func (*KeyValue) Descriptor() ([]byte, []int) { + return file_github_com_containerd_ttrpc_request_proto_rawDescGZIP(), []int{3} +} + +func (x *KeyValue) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *KeyValue) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +var File_github_com_containerd_ttrpc_request_proto protoreflect.FileDescriptor + +var file_github_com_containerd_ttrpc_request_proto_rawDesc = []byte{ + 0x0a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x74, 0x74, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, 0x74, 0x72, + 0x70, 0x63, 0x1a, 0x12, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa5, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4e, 0x61, 0x6e, + 0x6f, 0x12, 0x2b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x45, + 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x20, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x32, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1d, 0x5a, 0x1b, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x74, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_github_com_containerd_ttrpc_request_proto_rawDescOnce sync.Once + file_github_com_containerd_ttrpc_request_proto_rawDescData = file_github_com_containerd_ttrpc_request_proto_rawDesc +) + +func file_github_com_containerd_ttrpc_request_proto_rawDescGZIP() []byte { + file_github_com_containerd_ttrpc_request_proto_rawDescOnce.Do(func() { + file_github_com_containerd_ttrpc_request_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_containerd_ttrpc_request_proto_rawDescData) + }) + return file_github_com_containerd_ttrpc_request_proto_rawDescData +} + +var file_github_com_containerd_ttrpc_request_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_github_com_containerd_ttrpc_request_proto_goTypes = []interface{}{ + (*Request)(nil), // 0: ttrpc.Request + (*Response)(nil), // 1: ttrpc.Response + (*StringList)(nil), // 2: ttrpc.StringList + (*KeyValue)(nil), // 3: ttrpc.KeyValue + (*status.Status)(nil), // 4: Status +} +var file_github_com_containerd_ttrpc_request_proto_depIdxs = []int32{ + 3, // 0: ttrpc.Request.metadata:type_name -> ttrpc.KeyValue + 4, // 1: ttrpc.Response.status:type_name -> Status + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_github_com_containerd_ttrpc_request_proto_init() } +func file_github_com_containerd_ttrpc_request_proto_init() { + if File_github_com_containerd_ttrpc_request_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_github_com_containerd_ttrpc_request_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_containerd_ttrpc_request_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_containerd_ttrpc_request_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_containerd_ttrpc_request_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*KeyValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_containerd_ttrpc_request_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_containerd_ttrpc_request_proto_goTypes, + DependencyIndexes: file_github_com_containerd_ttrpc_request_proto_depIdxs, + MessageInfos: file_github_com_containerd_ttrpc_request_proto_msgTypes, + }.Build() + File_github_com_containerd_ttrpc_request_proto = out.File + file_github_com_containerd_ttrpc_request_proto_rawDesc = nil + file_github_com_containerd_ttrpc_request_proto_goTypes = nil + file_github_com_containerd_ttrpc_request_proto_depIdxs = nil +} diff --git a/vendor/github.com/containerd/ttrpc/request.proto b/vendor/github.com/containerd/ttrpc/request.proto new file mode 100644 index 000000000..37da334fc --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/request.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package ttrpc; + +import "proto/status.proto"; + +option go_package = "github.com/containerd/ttrpc"; + +message Request { + string service = 1; + string method = 2; + bytes payload = 3; + int64 timeout_nano = 4; + repeated KeyValue metadata = 5; +} + +message Response { + Status status = 1; + bytes payload = 2; +} + +message StringList { + repeated string list = 1; +} + +message KeyValue { + string key = 1; + string value = 2; +} diff --git a/vendor/github.com/containerd/ttrpc/server.go b/vendor/github.com/containerd/ttrpc/server.go new file mode 100644 index 000000000..9606a975d --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/server.go @@ -0,0 +1,587 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "context" + "errors" + "io" + "math/rand" + "net" + "sync" + "sync/atomic" + "syscall" + "time" + + "github.com/containerd/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type Server struct { + config *serverConfig + services *serviceSet + codec codec + + mu sync.Mutex + listeners map[net.Listener]struct{} + connections map[*serverConn]struct{} // all connections to current state + done chan struct{} // marks point at which we stop serving requests +} + +func NewServer(opts ...ServerOpt) (*Server, error) { + config := &serverConfig{} + for _, opt := range opts { + if err := opt(config); err != nil { + return nil, err + } + } + if config.interceptor == nil { + config.interceptor = defaultServerInterceptor + } + + return &Server{ + config: config, + services: newServiceSet(config.interceptor), + done: make(chan struct{}), + listeners: make(map[net.Listener]struct{}), + connections: make(map[*serverConn]struct{}), + }, nil +} + +// Register registers a map of methods to method handlers +// TODO: Remove in 2.0, does not support streams +func (s *Server) Register(name string, methods map[string]Method) { + s.services.register(name, &ServiceDesc{Methods: methods}) +} + +func (s *Server) RegisterService(name string, desc *ServiceDesc) { + s.services.register(name, desc) +} + +func (s *Server) Serve(ctx context.Context, l net.Listener) error { + s.mu.Lock() + s.addListenerLocked(l) + defer s.closeListener(l) + + select { + case <-s.done: + s.mu.Unlock() + return ErrServerClosed + default: + } + s.mu.Unlock() + + var ( + backoff time.Duration + handshaker = s.config.handshaker + ) + + if handshaker == nil { + handshaker = handshakerFunc(noopHandshake) + } + + for { + conn, err := l.Accept() + if err != nil { + select { + case <-s.done: + return ErrServerClosed + default: + } + + if terr, ok := err.(interface { + Temporary() bool + }); ok && terr.Temporary() { + if backoff == 0 { + backoff = time.Millisecond + } else { + backoff *= 2 + } + + backoff = min(time.Second, backoff) + + sleep := time.Duration(rand.Int63n(int64(backoff))) + log.G(ctx).WithError(err).Errorf("ttrpc: failed accept; backoff %v", sleep) + time.Sleep(sleep) + continue + } + + return err + } + + backoff = 0 + + approved, handshake, err := handshaker.Handshake(ctx, conn) + if err != nil { + log.G(ctx).WithError(err).Error("ttrpc: refusing connection after handshake") + conn.Close() + continue + } + + sc, err := s.newConn(approved, handshake) + if err != nil { + log.G(ctx).WithError(err).Error("ttrpc: create connection failed") + conn.Close() + continue + } + + go sc.run(ctx) + } +} + +func (s *Server) Shutdown(ctx context.Context) error { + s.mu.Lock() + select { + case <-s.done: + default: + // protected by mutex + close(s.done) + } + lnerr := s.closeListeners() + s.mu.Unlock() + + ticker := time.NewTicker(200 * time.Millisecond) + defer ticker.Stop() + for { + s.closeIdleConns() + + if s.countConnection() == 0 { + break + } + + select { + case <-ctx.Done(): + return ctx.Err() + case <-ticker.C: + } + } + + return lnerr +} + +// Close the server without waiting for active connections. +func (s *Server) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + + select { + case <-s.done: + default: + // protected by mutex + close(s.done) + } + + err := s.closeListeners() + for c := range s.connections { + c.close() + delete(s.connections, c) + } + + return err +} + +func (s *Server) addListenerLocked(l net.Listener) { + s.listeners[l] = struct{}{} +} + +func (s *Server) closeListener(l net.Listener) error { + s.mu.Lock() + defer s.mu.Unlock() + + return s.closeListenerLocked(l) +} + +func (s *Server) closeListenerLocked(l net.Listener) error { + defer delete(s.listeners, l) + return l.Close() +} + +func (s *Server) closeListeners() error { + var err error + for l := range s.listeners { + if cerr := s.closeListenerLocked(l); cerr != nil && err == nil { + err = cerr + } + } + return err +} + +func (s *Server) addConnection(c *serverConn) error { + s.mu.Lock() + defer s.mu.Unlock() + + select { + case <-s.done: + return ErrServerClosed + default: + } + + s.connections[c] = struct{}{} + return nil +} + +func (s *Server) delConnection(c *serverConn) { + s.mu.Lock() + defer s.mu.Unlock() + + delete(s.connections, c) +} + +func (s *Server) countConnection() int { + s.mu.Lock() + defer s.mu.Unlock() + + return len(s.connections) +} + +func (s *Server) closeIdleConns() { + s.mu.Lock() + defer s.mu.Unlock() + + for c := range s.connections { + if st, ok := c.getState(); !ok || st == connStateActive { + continue + } + c.close() + delete(s.connections, c) + } +} + +type connState int + +const ( + connStateActive = iota + 1 // outstanding requests + connStateIdle // no requests + connStateClosed // closed connection +) + +func (cs connState) String() string { + switch cs { + case connStateActive: + return "active" + case connStateIdle: + return "idle" + case connStateClosed: + return "closed" + default: + return "unknown" + } +} + +func (s *Server) newConn(conn net.Conn, handshake interface{}) (*serverConn, error) { + c := &serverConn{ + server: s, + conn: conn, + handshake: handshake, + shutdown: make(chan struct{}), + } + c.setState(connStateIdle) + if err := s.addConnection(c); err != nil { + c.close() + return nil, err + } + return c, nil +} + +type serverConn struct { + server *Server + conn net.Conn + handshake interface{} // data from handshake, not used for now + state atomic.Value + + shutdownOnce sync.Once + shutdown chan struct{} // forced shutdown, used by close +} + +func (c *serverConn) getState() (connState, bool) { + cs, ok := c.state.Load().(connState) + return cs, ok +} + +func (c *serverConn) setState(newstate connState) { + c.state.Store(newstate) +} + +func (c *serverConn) close() error { + c.shutdownOnce.Do(func() { + close(c.shutdown) + }) + + return nil +} + +func (c *serverConn) run(sctx context.Context) { + type ( + response struct { + id uint32 + status *status.Status + data []byte + closeStream bool + streaming bool + } + ) + + var ( + ch = newChannel(c.conn) + ctx, cancel = context.WithCancel(sctx) + state connState = connStateIdle + responses = make(chan response) + recvErr = make(chan error, 1) + done = make(chan struct{}) + streams = sync.Map{} + active int32 + lastStreamID uint32 + ) + + defer c.conn.Close() + defer cancel() + defer close(done) + defer c.server.delConnection(c) + + sendStatus := func(id uint32, st *status.Status) bool { + select { + case responses <- response{ + // even though we've had an invalid stream id, we send it + // back on the same stream id so the client knows which + // stream id was bad. + id: id, + status: st, + closeStream: true, + }: + return true + case <-c.shutdown: + return false + case <-done: + return false + } + } + + go func(recvErr chan error) { + defer close(recvErr) + for { + select { + case <-c.shutdown: + return + case <-done: + return + default: // proceed + } + + mh, p, err := ch.recv() + if err != nil { + status, ok := status.FromError(err) + if !ok { + recvErr <- err + return + } + + // in this case, we send an error for that particular message + // when the status is defined. + if !sendStatus(mh.StreamID, status) { + return + } + + continue + } + + if mh.StreamID%2 != 1 { + // enforce odd client initiated identifiers. + if !sendStatus(mh.StreamID, status.Newf(codes.InvalidArgument, "StreamID must be odd for client initiated streams")) { + return + } + continue + } + + if mh.Type == messageTypeData { + i, ok := streams.Load(mh.StreamID) + if !ok { + if !sendStatus(mh.StreamID, status.Newf(codes.InvalidArgument, "StreamID is no longer active")) { + return + } + continue + } + sh := i.(*streamHandler) + if mh.Flags&flagNoData != flagNoData { + unmarshal := func(obj interface{}) error { + err := protoUnmarshal(p, obj) + ch.putmbuf(p) + return err + } + + if err := sh.data(unmarshal); err != nil { + if !sendStatus(mh.StreamID, status.Newf(codes.InvalidArgument, "data handling error: %v", err)) { + return + } + continue + } + } + + if mh.Flags&flagRemoteClosed == flagRemoteClosed { + sh.closeSend() + if len(p) > 0 { + if !sendStatus(mh.StreamID, status.Newf(codes.InvalidArgument, "data close message cannot include data")) { + return + } + continue + } + } + } else if mh.Type == messageTypeRequest { + if mh.StreamID <= lastStreamID { + // enforce odd client initiated identifiers. + if !sendStatus(mh.StreamID, status.Newf(codes.InvalidArgument, "StreamID cannot be re-used and must increment")) { + return + } + continue + + } + lastStreamID = mh.StreamID + + // TODO: Make request type configurable + // Unmarshaller which takes in a byte array and returns an interface? + var req Request + if err := c.server.codec.Unmarshal(p, &req); err != nil { + ch.putmbuf(p) + if !sendStatus(mh.StreamID, status.Newf(codes.InvalidArgument, "unmarshal request error: %v", err)) { + return + } + continue + } + ch.putmbuf(p) + + id := mh.StreamID + respond := func(status *status.Status, data []byte, streaming, closeStream bool) error { + select { + case responses <- response{ + id: id, + status: status, + data: data, + closeStream: closeStream, + streaming: streaming, + }: + case <-done: + return ErrClosed + } + return nil + } + sh, err := c.server.services.handle(ctx, &req, respond) + if err != nil { + status, _ := status.FromError(err) + if !sendStatus(mh.StreamID, status) { + return + } + continue + } + + streams.Store(id, sh) + atomic.AddInt32(&active, 1) + } + // TODO: else we must ignore this for future compat. log this? + } + }(recvErr) + + for { + var ( + newstate connState + shutdown chan struct{} + ) + + activeN := atomic.LoadInt32(&active) + if activeN > 0 { + newstate = connStateActive + shutdown = nil + } else { + newstate = connStateIdle + shutdown = c.shutdown // only enable this branch in idle mode + } + if newstate != state { + c.setState(newstate) + state = newstate + } + + select { + case response := <-responses: + if !response.streaming || response.status.Code() != codes.OK { + p, err := c.server.codec.Marshal(&Response{ + Status: response.status.Proto(), + Payload: response.data, + }) + if err != nil { + log.G(ctx).WithError(err).Error("failed marshaling response") + return + } + + if err := ch.send(response.id, messageTypeResponse, 0, p); err != nil { + log.G(ctx).WithError(err).Error("failed sending message on channel") + return + } + } else { + var flags uint8 + if response.closeStream { + flags = flagRemoteClosed + } + if response.data == nil { + flags = flags | flagNoData + } + if err := ch.send(response.id, messageTypeData, flags, response.data); err != nil { + log.G(ctx).WithError(err).Error("failed sending message on channel") + return + } + } + + if response.closeStream { + // The ttrpc protocol currently does not support the case where + // the server is localClosed but not remoteClosed. Once the server + // is closing, the whole stream may be considered finished + streams.Delete(response.id) + atomic.AddInt32(&active, -1) + } + case err := <-recvErr: + // TODO(stevvooe): Not wildly clear what we should do in this + // branch. Basically, it means that we are no longer receiving + // requests due to a terminal error. + recvErr = nil // connection is now "closing" + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET) { + // The client went away and we should stop processing + // requests, so that the client connection is closed + return + } + log.G(ctx).WithError(err).Error("error receiving message") + // else, initiate shutdown + case <-shutdown: + return + } + } +} + +var noopFunc = func() {} + +func getRequestContext(ctx context.Context, req *Request) (retCtx context.Context, cancel func()) { + if len(req.Metadata) > 0 { + md := MD{} + md.fromRequest(req) + ctx = WithMetadata(ctx, md) + } + + cancel = noopFunc + if req.TimeoutNano == 0 { + return ctx, cancel + } + + ctx, cancel = context.WithTimeout(ctx, time.Duration(req.TimeoutNano)) + return ctx, cancel +} diff --git a/vendor/github.com/containerd/ttrpc/services.go b/vendor/github.com/containerd/ttrpc/services.go new file mode 100644 index 000000000..6d092bf95 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/services.go @@ -0,0 +1,279 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "path" + "unsafe" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +type Method func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) + +type StreamHandler func(context.Context, StreamServer) (interface{}, error) + +type Stream struct { + Handler StreamHandler + StreamingClient bool + StreamingServer bool +} + +type ServiceDesc struct { + Methods map[string]Method + Streams map[string]Stream +} + +type serviceSet struct { + services map[string]*ServiceDesc + unaryInterceptor UnaryServerInterceptor + streamInterceptor StreamServerInterceptor +} + +func newServiceSet(interceptor UnaryServerInterceptor) *serviceSet { + return &serviceSet{ + services: make(map[string]*ServiceDesc), + unaryInterceptor: interceptor, + streamInterceptor: defaultStreamServerInterceptor, + } +} + +func (s *serviceSet) register(name string, desc *ServiceDesc) { + if _, ok := s.services[name]; ok { + panic(fmt.Errorf("duplicate service %v registered", name)) + } + + s.services[name] = desc +} + +func (s *serviceSet) unaryCall(ctx context.Context, method Method, info *UnaryServerInfo, data []byte) (p []byte, st *status.Status) { + unmarshal := func(obj interface{}) error { + return protoUnmarshal(data, obj) + } + + resp, err := s.unaryInterceptor(ctx, unmarshal, info, method) + if err == nil { + if isNil(resp) { + err = errors.New("ttrpc: marshal called with nil") + } else { + p, err = protoMarshal(resp) + } + } + + st, ok := status.FromError(err) + if !ok { + st = status.New(convertCode(err), err.Error()) + } + + return p, st +} + +func (s *serviceSet) streamCall(ctx context.Context, stream StreamHandler, info *StreamServerInfo, ss StreamServer) (p []byte, st *status.Status) { + resp, err := s.streamInterceptor(ctx, ss, info, stream) + if err == nil { + p, err = protoMarshal(resp) + } + st, ok := status.FromError(err) + if !ok { + st = status.New(convertCode(err), err.Error()) + } + return +} + +func (s *serviceSet) handle(ctx context.Context, req *Request, respond func(*status.Status, []byte, bool, bool) error) (*streamHandler, error) { + srv, ok := s.services[req.Service] + if !ok { + return nil, status.Errorf(codes.Unimplemented, "service %v", req.Service) + } + + if method, ok := srv.Methods[req.Method]; ok { + go func() { + ctx, cancel := getRequestContext(ctx, req) + defer cancel() + + info := &UnaryServerInfo{ + FullMethod: fullPath(req.Service, req.Method), + } + p, st := s.unaryCall(ctx, method, info, req.Payload) + + respond(st, p, false, true) + }() + return nil, nil + } + if stream, ok := srv.Streams[req.Method]; ok { + ctx, cancel := getRequestContext(ctx, req) + info := &StreamServerInfo{ + FullMethod: fullPath(req.Service, req.Method), + StreamingClient: stream.StreamingClient, + StreamingServer: stream.StreamingServer, + } + sh := &streamHandler{ + ctx: ctx, + respond: respond, + recv: make(chan Unmarshaler, 5), + info: info, + } + go func() { + defer cancel() + p, st := s.streamCall(ctx, stream.Handler, info, sh) + respond(st, p, stream.StreamingServer, true) + }() + + // Empty proto messages serialized to 0 payloads, + // so signatures like: rpc Stream(google.protobuf.Empty) returns (stream Data); + // don't get invoked here, which causes hang on client side. + // See https://github.com/containerd/ttrpc/issues/126 + if req.Payload != nil || !info.StreamingClient { + unmarshal := func(obj interface{}) error { + return protoUnmarshal(req.Payload, obj) + } + if err := sh.data(unmarshal); err != nil { + return nil, err + } + } + + return sh, nil + } + return nil, status.Errorf(codes.Unimplemented, "method %v", req.Method) +} + +type streamHandler struct { + ctx context.Context + respond func(*status.Status, []byte, bool, bool) error + recv chan Unmarshaler + info *StreamServerInfo + + remoteClosed bool + localClosed bool +} + +func (s *streamHandler) closeSend() { + if !s.remoteClosed { + s.remoteClosed = true + close(s.recv) + } +} + +func (s *streamHandler) data(unmarshal Unmarshaler) error { + if s.remoteClosed { + return ErrStreamClosed + } + select { + case s.recv <- unmarshal: + return nil + case <-s.ctx.Done(): + return s.ctx.Err() + } +} + +func (s *streamHandler) SendMsg(m interface{}) error { + if s.localClosed { + return ErrStreamClosed + } + p, err := protoMarshal(m) + if err != nil { + return err + } + return s.respond(nil, p, true, false) +} + +func (s *streamHandler) RecvMsg(m interface{}) error { + select { + case unmarshal, ok := <-s.recv: + if !ok { + return io.EOF + } + return unmarshal(m) + case <-s.ctx.Done(): + return s.ctx.Err() + + } +} + +func protoUnmarshal(p []byte, obj interface{}) error { + switch v := obj.(type) { + case proto.Message: + if err := proto.Unmarshal(p, v); err != nil { + return status.Errorf(codes.Internal, "ttrpc: error unmarshalling payload: %v", err.Error()) + } + default: + return status.Errorf(codes.Internal, "ttrpc: error unsupported request type: %T", v) + } + return nil +} + +func protoMarshal(obj interface{}) ([]byte, error) { + if obj == nil { + return nil, nil + } + + switch v := obj.(type) { + case proto.Message: + r, err := proto.Marshal(v) + if err != nil { + return nil, status.Errorf(codes.Internal, "ttrpc: error marshaling payload: %v", err.Error()) + } + + return r, nil + default: + return nil, status.Errorf(codes.Internal, "ttrpc: error unsupported response type: %T", v) + } +} + +// convertCode maps stdlib go errors into grpc space. +// +// This is ripped from the grpc-go code base. +func convertCode(err error) codes.Code { + switch err { + case nil: + return codes.OK + case io.EOF: + return codes.OutOfRange + case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF: + return codes.FailedPrecondition + case os.ErrInvalid: + return codes.InvalidArgument + case context.Canceled: + return codes.Canceled + case context.DeadlineExceeded: + return codes.DeadlineExceeded + } + switch { + case os.IsExist(err): + return codes.AlreadyExists + case os.IsNotExist(err): + return codes.NotFound + case os.IsPermission(err): + return codes.PermissionDenied + } + return codes.Unknown +} + +func fullPath(service, method string) string { + return "/" + path.Join(service, method) +} + +func isNil(resp interface{}) bool { + return (*[2]uintptr)(unsafe.Pointer(&resp))[1] == 0 +} diff --git a/vendor/github.com/containerd/ttrpc/stream.go b/vendor/github.com/containerd/ttrpc/stream.go new file mode 100644 index 000000000..739a4c967 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/stream.go @@ -0,0 +1,84 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "context" + "sync" +) + +type streamID uint32 + +type streamMessage struct { + header messageHeader + payload []byte +} + +type stream struct { + id streamID + sender sender + recv chan *streamMessage + + closeOnce sync.Once + recvErr error + recvClose chan struct{} +} + +func newStream(id streamID, send sender) *stream { + return &stream{ + id: id, + sender: send, + recv: make(chan *streamMessage, 1), + recvClose: make(chan struct{}), + } +} + +func (s *stream) closeWithError(err error) error { + s.closeOnce.Do(func() { + if err != nil { + s.recvErr = err + } else { + s.recvErr = ErrClosed + } + close(s.recvClose) + }) + return nil +} + +func (s *stream) send(mt messageType, flags uint8, b []byte) error { + return s.sender.send(uint32(s.id), mt, flags, b) +} + +func (s *stream) receive(ctx context.Context, msg *streamMessage) error { + select { + case <-s.recvClose: + return s.recvErr + default: + } + select { + case <-s.recvClose: + return s.recvErr + case s.recv <- msg: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} + +type sender interface { + send(uint32, messageType, uint8, []byte) error +} diff --git a/vendor/github.com/containerd/ttrpc/stream_server.go b/vendor/github.com/containerd/ttrpc/stream_server.go new file mode 100644 index 000000000..b6d1ba720 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/stream_server.go @@ -0,0 +1,22 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +type StreamServer interface { + SendMsg(m interface{}) error + RecvMsg(m interface{}) error +} diff --git a/vendor/github.com/containerd/ttrpc/test.proto b/vendor/github.com/containerd/ttrpc/test.proto new file mode 100644 index 000000000..0e114d556 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/test.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package ttrpc; + +option go_package = "github.com/containerd/ttrpc/internal"; + +message TestPayload { + string foo = 1; + int64 deadline = 2; + string metadata = 3; +} + +message EchoPayload { + int64 seq = 1; + string msg = 2; +} diff --git a/vendor/github.com/containerd/ttrpc/unixcreds_linux.go b/vendor/github.com/containerd/ttrpc/unixcreds_linux.go new file mode 100644 index 000000000..c82c9f9d4 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/unixcreds_linux.go @@ -0,0 +1,105 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ttrpc + +import ( + "context" + "errors" + "fmt" + "net" + "os" + "syscall" + + "golang.org/x/sys/unix" +) + +type UnixCredentialsFunc func(*unix.Ucred) error + +func (fn UnixCredentialsFunc) Handshake(_ context.Context, conn net.Conn) (net.Conn, interface{}, error) { + uc, err := requireUnixSocket(conn) + if err != nil { + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: require unix socket: %w", err) + } + + rs, err := uc.SyscallConn() + if err != nil { + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: (net.UnixConn).SyscallConn failed: %w", err) + } + var ( + ucred *unix.Ucred + ucredErr error + ) + if err := rs.Control(func(fd uintptr) { + ucred, ucredErr = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED) + }); err != nil { + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: (*syscall.RawConn).Control failed: %w", err) + } + + if ucredErr != nil { + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: failed to retrieve socket peer credentials: %w", ucredErr) + } + + if err := fn(ucred); err != nil { + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: credential check failed: %w", err) + } + + return uc, ucred, nil +} + +// UnixSocketRequireUidGid requires specific *effective* UID/GID, rather than the real UID/GID. +// +// For example, if a daemon binary is owned by the root (UID 0) with SUID bit but running as an +// unprivileged user (UID 1001), the effective UID becomes 0, and the real UID becomes 1001. +// So calling this function with uid=0 allows a connection from effective UID 0 but rejects +// a connection from effective UID 1001. +// +// See socket(7), SO_PEERCRED: "The returned credentials are those that were in effect at the time of the call to connect(2) or socketpair(2)." +func UnixSocketRequireUidGid(uid, gid int) UnixCredentialsFunc { + return func(ucred *unix.Ucred) error { + return requireUidGid(ucred, uid, gid) + } +} + +func UnixSocketRequireRoot() UnixCredentialsFunc { + return UnixSocketRequireUidGid(0, 0) +} + +// UnixSocketRequireSameUser resolves the current effective unix user and returns a +// UnixCredentialsFunc that will validate incoming unix connections against the +// current credentials. +// +// This is useful when using abstract sockets that are accessible by all users. +func UnixSocketRequireSameUser() UnixCredentialsFunc { + euid, egid := os.Geteuid(), os.Getegid() + return UnixSocketRequireUidGid(euid, egid) +} + +func requireUidGid(ucred *unix.Ucred, uid, gid int) error { + if (uid != -1 && uint32(uid) != ucred.Uid) || (gid != -1 && uint32(gid) != ucred.Gid) { + return fmt.Errorf("ttrpc: invalid credentials: %v", syscall.EPERM) + } + return nil +} + +func requireUnixSocket(conn net.Conn) (*net.UnixConn, error) { + uc, ok := conn.(*net.UnixConn) + if !ok { + return nil, errors.New("a unix socket connection is required") + } + + return uc, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 1891f6f1d..0737efb53 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -241,10 +241,21 @@ github.com/cncf/xds/go/xds/data/orca/v3 github.com/cncf/xds/go/xds/service/orca/v3 github.com/cncf/xds/go/xds/type/matcher/v3 github.com/cncf/xds/go/xds/type/v3 +# github.com/containerd/containerd/api v1.11.1 +## explicit; go 1.24.0 +github.com/containerd/containerd/api/runtime/task/v2 +github.com/containerd/containerd/api/types +github.com/containerd/containerd/api/types/task +# github.com/containerd/log v0.1.0 +## explicit; go 1.20 +github.com/containerd/log # github.com/containerd/stargz-snapshotter/estargz v0.18.2 ## explicit; go 1.24.0 github.com/containerd/stargz-snapshotter/estargz github.com/containerd/stargz-snapshotter/estargz/errorutil +# github.com/containerd/ttrpc v1.2.8 +## explicit; go 1.22 +github.com/containerd/ttrpc # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew