1 package podman
2
3 import (
4 "bufio"
5 "context"
6 "fmt"
7 "os/exec"
8 "strings"
9 "time"
10
11 envcontext "github.com/redhat-developer/odo/pkg/config/context"
12 "github.com/redhat-developer/odo/pkg/platform"
13
14 corev1 "k8s.io/api/core/v1"
15 jsonserializer "k8s.io/apimachinery/pkg/runtime/serializer/json"
16 "k8s.io/klog"
17 "k8s.io/kubectl/pkg/scheme"
18 )
19
20 type PodmanCli struct {
21 podmanCmd string
22 podmanCmdInitTimeout time.Duration
23 containerRunGlobalExtraArgs []string
24 containerRunExtraArgs []string
25 }
26
27 var _ Client = (*PodmanCli)(nil)
28 var _ platform.Client = (*PodmanCli)(nil)
29
30
31 func NewPodmanCli(ctx context.Context) (*PodmanCli, error) {
32
33 cli := &PodmanCli{
34 podmanCmd: envcontext.GetEnvConfig(ctx).PodmanCmd,
35 podmanCmdInitTimeout: envcontext.GetEnvConfig(ctx).PodmanCmdInitTimeout,
36 containerRunGlobalExtraArgs: envcontext.GetEnvConfig(ctx).OdoContainerBackendGlobalArgs,
37 containerRunExtraArgs: envcontext.GetEnvConfig(ctx).OdoContainerRunArgs,
38 }
39 version, err := cli.Version(ctx)
40 if err != nil {
41 return nil, err
42 }
43 if version.Client == nil {
44 return nil, fmt.Errorf("executable %q not recognized as podman client", cli.podmanCmd)
45 }
46
47 return cli, nil
48 }
49
50 func (o *PodmanCli) PlayKube(pod *corev1.Pod) error {
51 serializer := jsonserializer.NewSerializerWithOptions(
52 jsonserializer.SimpleMetaFactory{},
53 scheme.Scheme,
54 scheme.Scheme,
55 jsonserializer.SerializerOptions{
56 Yaml: true,
57 },
58 )
59
60
61 args := make([]string, 0, len(o.containerRunGlobalExtraArgs)+len(o.containerRunExtraArgs)+3)
62 args = append(args, o.containerRunGlobalExtraArgs...)
63 args = append(args, "play", "kube")
64 args = append(args, o.containerRunExtraArgs...)
65 args = append(args, "-")
66
67 cmd := exec.Command(o.podmanCmd, args...)
68 klog.V(3).Infof("executing %v", cmd.Args)
69 stdin, err := cmd.StdinPipe()
70 if err != nil {
71 return err
72 }
73
74 stdout, err := cmd.StdoutPipe()
75 if err != nil {
76 return err
77 }
78 cmd.Stderr = cmd.Stdout
79
80 if err = cmd.Start(); err != nil {
81 return err
82 }
83
84 if klog.V(4) {
85 var sb strings.Builder
86 _ = serializer.Encode(pod, &sb)
87 klog.Infof("Pod spec to play: \n---\n%s\n---\n", sb.String())
88 }
89
90 err = serializer.Encode(pod, stdin)
91 if err != nil {
92 return err
93 }
94 stdin.Close()
95 var podmanOut string
96 go func() {
97 for {
98 tmp := make([]byte, 1024)
99 _, err = stdout.Read(tmp)
100 podmanOut += string(tmp)
101 klog.V(4).Info(string(tmp))
102 if err != nil {
103 break
104 }
105 }
106 }()
107 if err = cmd.Wait(); err != nil {
108 if exiterr, ok := err.(*exec.ExitError); ok {
109 err = fmt.Errorf("%s: %s\nComplete Podman output:\n%s", err, string(exiterr.Stderr), podmanOut)
110 }
111 return err
112 }
113
114 return nil
115 }
116
117 func (o *PodmanCli) KubeGenerate(name string) (*corev1.Pod, error) {
118 serializer := jsonserializer.NewSerializerWithOptions(
119 jsonserializer.SimpleMetaFactory{},
120 scheme.Scheme,
121 scheme.Scheme,
122 jsonserializer.SerializerOptions{
123 Yaml: true,
124 },
125 )
126
127 cmd := exec.Command(o.podmanCmd, append(o.containerRunGlobalExtraArgs, "generate", "kube", name)...)
128 klog.V(3).Infof("executing %v", cmd.Args)
129 resultBytes, err := cmd.Output()
130 if err != nil {
131 if exiterr, ok := err.(*exec.ExitError); ok {
132 err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr))
133 }
134 return nil, err
135 }
136 var pod corev1.Pod
137 _, _, err = serializer.Decode(resultBytes, nil, &pod)
138 if err != nil {
139 return nil, err
140 }
141 return &pod, nil
142 }
143
144 func (o *PodmanCli) PodStop(podname string) error {
145 cmd := exec.Command(o.podmanCmd, append(o.containerRunGlobalExtraArgs, "pod", "stop", podname)...)
146 klog.V(3).Infof("executing %v", cmd.Args)
147 out, err := cmd.Output()
148 if err != nil {
149 if exiterr, ok := err.(*exec.ExitError); ok {
150 err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr))
151 }
152 return err
153 }
154 klog.V(4).Infof("Stopped pod %s", string(out))
155 return nil
156 }
157
158 func (o *PodmanCli) PodRm(podname string) error {
159 cmd := exec.Command(o.podmanCmd, append(o.containerRunGlobalExtraArgs, "pod", "rm", podname)...)
160 klog.V(3).Infof("executing %v", cmd.Args)
161 out, err := cmd.Output()
162 if err != nil {
163 if exiterr, ok := err.(*exec.ExitError); ok {
164 err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr))
165 }
166 return err
167 }
168 klog.V(4).Infof("Deleted pod %s", string(out))
169 return nil
170 }
171
172 func (o *PodmanCli) PodLs() (map[string]bool, error) {
173 cmd := exec.Command(o.podmanCmd, append(o.containerRunGlobalExtraArgs, "pod", "list", "--format", "{{.Name}}", "--noheading")...)
174 klog.V(3).Infof("executing %v", cmd.Args)
175 out, err := cmd.Output()
176 if err != nil {
177 if exiterr, ok := err.(*exec.ExitError); ok {
178 err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr))
179 }
180 return nil, err
181 }
182 return SplitLinesAsSet(string(out)), nil
183 }
184
185 func (o *PodmanCli) VolumeRm(volumeName string) error {
186 cmd := exec.Command(o.podmanCmd, append(o.containerRunGlobalExtraArgs, "volume", "rm", volumeName)...)
187 klog.V(3).Infof("executing %v", cmd.Args)
188 out, err := cmd.Output()
189 if err != nil {
190 if exiterr, ok := err.(*exec.ExitError); ok {
191 err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr))
192 }
193 return err
194 }
195 klog.V(4).Infof("Deleted volume %s", string(out))
196 return nil
197 }
198
199 func (o *PodmanCli) VolumeLs() (map[string]bool, error) {
200 cmd := exec.Command(o.podmanCmd, append(o.containerRunGlobalExtraArgs, "volume", "ls", "--format", "{{.Name}}", "--noheading")...)
201 klog.V(3).Infof("executing %v", cmd.Args)
202 out, err := cmd.Output()
203 if err != nil {
204 if exiterr, ok := err.(*exec.ExitError); ok {
205 err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr))
206 }
207 return nil, err
208 }
209 return SplitLinesAsSet(string(out)), nil
210 }
211
212 func (o *PodmanCli) CleanupPodResources(pod *corev1.Pod, cleanupVolumes bool) error {
213 err := o.PodStop(pod.GetName())
214 if err != nil {
215 return err
216 }
217 err = o.PodRm(pod.GetName())
218 if err != nil {
219 return err
220 }
221
222 if !cleanupVolumes {
223 return nil
224 }
225
226 for _, volume := range pod.Spec.Volumes {
227 if volume.PersistentVolumeClaim == nil {
228 continue
229 }
230 volumeName := volume.PersistentVolumeClaim.ClaimName
231 klog.V(3).Infof("deleting podman volume %q", volumeName)
232 err = o.VolumeRm(volumeName)
233 if err != nil {
234 return err
235 }
236 }
237 return nil
238 }
239
240 func SplitLinesAsSet(s string) map[string]bool {
241 lines := map[string]bool{}
242 sc := bufio.NewScanner(strings.NewReader(s))
243 for sc.Scan() {
244 lines[sc.Text()] = true
245 }
246 return lines
247 }
248
View as plain text