github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/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 } 119 client := ttrpc.NewClient(conn, ttrpc.WithOnClose(onCloseWithShimLog)) 120 return &shim{ 121 bundle: b.bundle, 122 client: client, 123 task: task.NewTaskClient(client), 124 events: b.events, 125 rtTasks: b.rtTasks, 126 }, nil 127 } 128 129 func (b *binary) Delete(ctx context.Context) (*runtime.Exit, error) { 130 log.G(ctx).Info("cleaning up dead shim") 131 132 // Windows cannot delete the current working directory while an 133 // executable is in use with it. For the cleanup case we invoke with the 134 // default work dir and forward the bundle path on the cmdline. 135 var bundlePath string 136 if gruntime.GOOS != "windows" { 137 bundlePath = b.bundle.Path 138 } 139 140 cmd, err := client.Command(ctx, 141 b.runtime, 142 b.containerdAddress, 143 b.containerdTTRPCAddress, 144 bundlePath, 145 nil, 146 "-id", b.bundle.ID, 147 "-bundle", b.bundle.Path, 148 "delete") 149 if err != nil { 150 return nil, err 151 } 152 var ( 153 out = bytes.NewBuffer(nil) 154 errb = bytes.NewBuffer(nil) 155 ) 156 cmd.Stdout = out 157 cmd.Stderr = errb 158 if err := cmd.Run(); err != nil { 159 return nil, errors.Wrapf(err, "%s", errb.String()) 160 } 161 s := errb.String() 162 if s != "" { 163 log.G(ctx).Warnf("cleanup warnings %s", s) 164 } 165 var response task.DeleteResponse 166 if err := response.Unmarshal(out.Bytes()); err != nil { 167 return nil, err 168 } 169 if err := b.bundle.Delete(); err != nil { 170 return nil, err 171 } 172 return &runtime.Exit{ 173 Status: response.ExitStatus, 174 Timestamp: response.ExitedAt, 175 Pid: response.Pid, 176 }, nil 177 }