github.com/containerd/Containerd@v1.4.13/runtime/v2/binary.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package v2 18 19 import ( 20 "bytes" 21 "context" 22 "io" 23 "os" 24 gruntime "runtime" 25 "strings" 26 27 "github.com/containerd/containerd/events/exchange" 28 "github.com/containerd/containerd/log" 29 "github.com/containerd/containerd/namespaces" 30 "github.com/containerd/containerd/runtime" 31 client "github.com/containerd/containerd/runtime/v2/shim" 32 "github.com/containerd/containerd/runtime/v2/task" 33 "github.com/containerd/ttrpc" 34 "github.com/gogo/protobuf/types" 35 "github.com/pkg/errors" 36 "github.com/sirupsen/logrus" 37 ) 38 39 func shimBinary(ctx context.Context, bundle *Bundle, runtime, containerdAddress string, containerdTTRPCAddress string, events *exchange.Exchange, rt *runtime.TaskList) *binary { 40 return &binary{ 41 bundle: bundle, 42 runtime: runtime, 43 containerdAddress: containerdAddress, 44 containerdTTRPCAddress: containerdTTRPCAddress, 45 events: events, 46 rtTasks: rt, 47 } 48 } 49 50 type binary struct { 51 runtime string 52 containerdAddress string 53 containerdTTRPCAddress string 54 bundle *Bundle 55 events *exchange.Exchange 56 rtTasks *runtime.TaskList 57 } 58 59 func (b *binary) Start(ctx context.Context, opts *types.Any, onClose func()) (_ *shim, err error) { 60 args := []string{"-id", b.bundle.ID} 61 if logrus.GetLevel() == logrus.DebugLevel { 62 args = append(args, "-debug") 63 } 64 args = append(args, "start") 65 66 cmd, err := client.Command( 67 ctx, 68 b.runtime, 69 b.containerdAddress, 70 b.containerdTTRPCAddress, 71 b.bundle.Path, 72 opts, 73 args..., 74 ) 75 if err != nil { 76 return nil, err 77 } 78 // Windows needs a namespace when openShimLog 79 ns, _ := namespaces.Namespace(ctx) 80 shimCtx, cancelShimLog := context.WithCancel(namespaces.WithNamespace(context.Background(), ns)) 81 defer func() { 82 if err != nil { 83 cancelShimLog() 84 } 85 }() 86 f, err := openShimLog(shimCtx, b.bundle, client.AnonDialer) 87 if err != nil { 88 return nil, errors.Wrap(err, "open shim log pipe") 89 } 90 defer func() { 91 if err != nil { 92 f.Close() 93 } 94 }() 95 // open the log pipe and block until the writer is ready 96 // this helps with synchronization of the shim 97 // copy the shim's logs to containerd's output 98 go func() { 99 defer f.Close() 100 _, err := io.Copy(os.Stderr, f) 101 err = checkCopyShimLogError(ctx, err) 102 if err != nil { 103 log.G(ctx).WithError(err).Error("copy shim log") 104 } 105 }() 106 out, err := cmd.CombinedOutput() 107 if err != nil { 108 return nil, errors.Wrapf(err, "%s", out) 109 } 110 address := strings.TrimSpace(string(out)) 111 conn, err := client.Connect(address, client.AnonDialer) 112 if err != nil { 113 return nil, err 114 } 115 onCloseWithShimLog := func() { 116 onClose() 117 cancelShimLog() 118 f.Close() 119 } 120 client := ttrpc.NewClient(conn, ttrpc.WithOnClose(onCloseWithShimLog)) 121 return &shim{ 122 bundle: b.bundle, 123 client: client, 124 task: task.NewTaskClient(client), 125 events: b.events, 126 rtTasks: b.rtTasks, 127 }, nil 128 } 129 130 func (b *binary) Delete(ctx context.Context) (*runtime.Exit, error) { 131 log.G(ctx).Info("cleaning up dead shim") 132 133 // Windows cannot delete the current working directory while an 134 // executable is in use with it. For the cleanup case we invoke with the 135 // default work dir and forward the bundle path on the cmdline. 136 var bundlePath string 137 if gruntime.GOOS != "windows" { 138 bundlePath = b.bundle.Path 139 } 140 141 cmd, err := client.Command(ctx, 142 b.runtime, 143 b.containerdAddress, 144 b.containerdTTRPCAddress, 145 bundlePath, 146 nil, 147 "-id", b.bundle.ID, 148 "-bundle", b.bundle.Path, 149 "delete") 150 if err != nil { 151 return nil, err 152 } 153 var ( 154 out = bytes.NewBuffer(nil) 155 errb = bytes.NewBuffer(nil) 156 ) 157 cmd.Stdout = out 158 cmd.Stderr = errb 159 if err := cmd.Run(); err != nil { 160 return nil, errors.Wrapf(err, "%s", errb.String()) 161 } 162 s := errb.String() 163 if s != "" { 164 log.G(ctx).Warnf("cleanup warnings %s", s) 165 } 166 var response task.DeleteResponse 167 if err := response.Unmarshal(out.Bytes()); err != nil { 168 return nil, err 169 } 170 if err := b.bundle.Delete(); err != nil { 171 return nil, err 172 } 173 return &runtime.Exit{ 174 Status: response.ExitStatus, 175 Timestamp: response.ExitedAt, 176 Pid: response.Pid, 177 }, nil 178 }