github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/infra/tunnel/images.go (about) 1 package tunnel 2 3 import ( 4 "context" 5 "io/ioutil" 6 "os" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/containers/common/libimage" 12 "github.com/containers/common/pkg/config" 13 "github.com/containers/image/v5/docker/reference" 14 "github.com/containers/image/v5/types" 15 "github.com/hanks177/podman/v4/libpod/define" 16 "github.com/hanks177/podman/v4/pkg/bindings/images" 17 "github.com/hanks177/podman/v4/pkg/domain/entities" 18 "github.com/hanks177/podman/v4/pkg/domain/entities/reports" 19 "github.com/hanks177/podman/v4/pkg/domain/utils" 20 "github.com/hanks177/podman/v4/pkg/errorhandling" 21 utils2 "github.com/hanks177/podman/v4/utils" 22 "github.com/pkg/errors" 23 ) 24 25 func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) { 26 found, err := images.Exists(ir.ClientCtx, nameOrID, nil) 27 return &entities.BoolReport{Value: found}, err 28 } 29 30 func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) { 31 options := new(images.RemoveOptions).WithForce(opts.Force).WithIgnore(opts.Ignore).WithAll(opts.All) 32 return images.Remove(ir.ClientCtx, imagesArg, options) 33 } 34 35 func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { 36 filters := make(map[string][]string, len(opts.Filter)) 37 for _, filter := range opts.Filter { 38 f := strings.Split(filter, "=") 39 filters[f[0]] = f[1:] 40 } 41 options := new(images.ListOptions).WithAll(opts.All).WithFilters(filters) 42 psImages, err := images.List(ir.ClientCtx, options) 43 if err != nil { 44 return nil, err 45 } 46 47 is := make([]*entities.ImageSummary, len(psImages)) 48 for i, img := range psImages { 49 hold := entities.ImageSummary{} 50 if err := utils.DeepCopy(&hold, img); err != nil { 51 return nil, err 52 } 53 is[i] = &hold 54 } 55 return is, nil 56 } 57 58 func (ir *ImageEngine) Mount(ctx context.Context, images []string, options entities.ImageMountOptions) ([]*entities.ImageMountReport, error) { 59 return nil, errors.New("mounting images is not supported for remote clients") 60 } 61 62 func (ir *ImageEngine) Unmount(ctx context.Context, images []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) { 63 return nil, errors.New("unmounting images is not supported for remote clients") 64 } 65 66 func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { 67 options := new(images.HistoryOptions) 68 results, err := images.History(ir.ClientCtx, nameOrID, options) 69 if err != nil { 70 return nil, err 71 } 72 73 history := entities.ImageHistoryReport{ 74 Layers: make([]entities.ImageHistoryLayer, len(results)), 75 } 76 77 for i, layer := range results { 78 // Created time comes over as an int64 so needs conversion to time.time 79 t := time.Unix(layer.Created, 0) 80 hold := entities.ImageHistoryLayer{ 81 ID: layer.ID, 82 Created: t.UTC(), 83 CreatedBy: layer.CreatedBy, 84 Tags: layer.Tags, 85 Size: layer.Size, 86 Comment: layer.Comment, 87 } 88 history.Layers[i] = hold 89 } 90 return &history, nil 91 } 92 93 func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) { 94 filters := make(map[string][]string, len(opts.Filter)) 95 for _, filter := range opts.Filter { 96 f := strings.Split(filter, "=") 97 filters[f[0]] = f[1:] 98 } 99 options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External) 100 reports, err := images.Prune(ir.ClientCtx, options) 101 if err != nil { 102 return nil, err 103 } 104 return reports, nil 105 } 106 107 func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) { 108 options := new(images.PullOptions) 109 options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithArch(opts.Arch).WithOS(opts.OS) 110 options.WithVariant(opts.Variant).WithPassword(opts.Password) 111 options.WithQuiet(opts.Quiet).WithUsername(opts.Username).WithPolicy(opts.PullPolicy.String()) 112 if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { 113 if s == types.OptionalBoolTrue { 114 options.WithSkipTLSVerify(true) 115 } else { 116 options.WithSkipTLSVerify(false) 117 } 118 } 119 pulledImages, err := images.Pull(ir.ClientCtx, rawImage, options) 120 if err != nil { 121 return nil, err 122 } 123 return &entities.ImagePullReport{Images: pulledImages}, nil 124 } 125 126 type pullResult struct { 127 images []*libimage.Image 128 err error 129 } 130 131 func (ir *ImageEngine) PullImage(ctx context.Context, rawImage string, pullOptions *libimage.PullOptions) error { 132 return errors.New("not support yet") 133 } 134 135 func (ir *ImageEngine) Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error { 136 return errors.Wrapf(define.ErrNotImplemented, "cannot use the remote client to transfer images between root and rootless storage") 137 } 138 139 func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageTagOptions) error { 140 options := new(images.TagOptions) 141 for _, newTag := range tags { 142 var ( 143 tag, repo string 144 ) 145 ref, err := reference.Parse(newTag) 146 if err != nil { 147 return errors.Wrapf(err, "error parsing reference %q", newTag) 148 } 149 if t, ok := ref.(reference.Tagged); ok { 150 tag = t.Tag() 151 } 152 if r, ok := ref.(reference.Named); ok { 153 repo = r.Name() 154 } 155 if len(repo) < 1 { 156 return errors.Errorf("invalid image name %q", nameOrID) 157 } 158 if err := images.Tag(ir.ClientCtx, nameOrID, tag, repo, options); err != nil { 159 return err 160 } 161 } 162 return nil 163 } 164 165 func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageUntagOptions) error { 166 options := new(images.UntagOptions) 167 if len(tags) == 0 { 168 return images.Untag(ir.ClientCtx, nameOrID, "", "", options) 169 } 170 171 for _, newTag := range tags { 172 var ( 173 tag, repo string 174 ) 175 ref, err := reference.Parse(newTag) 176 if err != nil { 177 return errors.Wrapf(err, "error parsing reference %q", newTag) 178 } 179 if t, ok := ref.(reference.Tagged); ok { 180 tag = t.Tag() 181 } 182 if t, ok := ref.(reference.Digested); ok { 183 tag += "@" + t.Digest().String() 184 } 185 if r, ok := ref.(reference.Named); ok { 186 repo = r.Name() 187 } 188 if len(repo) < 1 { 189 return errors.Errorf("invalid image name %q", nameOrID) 190 } 191 if err := images.Untag(ir.ClientCtx, nameOrID, tag, repo, options); err != nil { 192 return err 193 } 194 } 195 return nil 196 } 197 198 func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) { 199 options := new(images.GetOptions).WithSize(opts.Size) 200 reports := []*entities.ImageInspectReport{} 201 errs := []error{} 202 for _, i := range namesOrIDs { 203 r, err := images.GetImage(ir.ClientCtx, i, options) 204 if err != nil { 205 errModel, ok := err.(*errorhandling.ErrorModel) 206 if !ok { 207 return nil, nil, err 208 } 209 if errModel.ResponseCode == 404 { 210 errs = append(errs, errors.Wrapf(err, "unable to inspect %q", i)) 211 continue 212 } 213 return nil, nil, err 214 } 215 reports = append(reports, r) 216 } 217 return reports, errs, nil 218 } 219 220 func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) { 221 f, err := os.Open(opts.Input) 222 if err != nil { 223 return nil, err 224 } 225 defer f.Close() 226 fInfo, err := f.Stat() 227 if err != nil { 228 return nil, err 229 } 230 if fInfo.IsDir() { 231 return nil, errors.Errorf("remote client supports archives only but %q is a directory", opts.Input) 232 } 233 return images.Load(ir.ClientCtx, f) 234 } 235 236 func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { 237 var ( 238 err error 239 f *os.File 240 ) 241 options := new(images.ImportOptions).WithChanges(opts.Changes).WithMessage(opts.Message).WithReference(opts.Reference) 242 options.WithOS(opts.OS).WithArchitecture(opts.Architecture).WithVariant(opts.Variant) 243 if opts.SourceIsURL { 244 options.WithURL(opts.Source) 245 } else { 246 f, err = os.Open(opts.Source) 247 if err != nil { 248 return nil, err 249 } 250 } 251 return images.Import(ir.ClientCtx, f, options) 252 } 253 254 func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, opts entities.ImagePushOptions) error { 255 options := new(images.PushOptions) 256 options.WithAll(opts.All).WithCompress(opts.Compress).WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithFormat(opts.Format) 257 258 if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { 259 if s == types.OptionalBoolTrue { 260 options.WithSkipTLSVerify(true) 261 } else { 262 options.WithSkipTLSVerify(false) 263 } 264 } 265 return images.Push(ir.ClientCtx, source, destination, options) 266 } 267 268 func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, opts entities.ImageSaveOptions) error { 269 var ( 270 f *os.File 271 err error 272 ) 273 options := new(images.ExportOptions).WithFormat(opts.Format).WithCompress(opts.Compress) 274 options = options.WithOciAcceptUncompressedLayers(opts.OciAcceptUncompressedLayers) 275 276 switch opts.Format { 277 case "oci-dir", "docker-dir": 278 f, err = ioutil.TempFile("", "podman_save") 279 if err == nil { 280 defer func() { _ = os.Remove(f.Name()) }() 281 } 282 default: 283 // This code was added to allow for opening stdout replacing 284 // os.Create(opts.Output) which was attempting to open the file 285 // for read/write which fails on Darwin platforms 286 f, err = os.OpenFile(opts.Output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 287 } 288 if err != nil { 289 return err 290 } 291 292 exErr := images.Export(ir.ClientCtx, append([]string{nameOrID}, tags...), f, options) 293 if err := f.Close(); err != nil { 294 return err 295 } 296 if exErr != nil { 297 return exErr 298 } 299 300 if opts.Format != "oci-dir" && opts.Format != "docker-dir" { 301 return nil 302 } 303 304 f, err = os.Open(f.Name()) 305 if err != nil { 306 return err 307 } 308 info, err := os.Stat(opts.Output) 309 switch { 310 case err == nil: 311 if info.Mode().IsRegular() { 312 return errors.Errorf("%q already exists as a regular file", opts.Output) 313 } 314 case os.IsNotExist(err): 315 if err := os.Mkdir(opts.Output, 0755); err != nil { 316 return err 317 } 318 default: 319 return err 320 } 321 return utils2.UntarToFileSystem(opts.Output, f, nil) 322 } 323 324 func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { 325 mappedFilters := make(map[string][]string) 326 filters, err := libimage.ParseSearchFilter(opts.Filters) 327 if err != nil { 328 return nil, err 329 } 330 if stars := filters.Stars; stars > 0 { 331 mappedFilters["stars"] = []string{strconv.Itoa(stars)} 332 } 333 334 if official := filters.IsOfficial; official != types.OptionalBoolUndefined { 335 mappedFilters["is-official"] = []string{strconv.FormatBool(official == types.OptionalBoolTrue)} 336 } 337 338 if automated := filters.IsAutomated; automated != types.OptionalBoolUndefined { 339 mappedFilters["is-automated"] = []string{strconv.FormatBool(automated == types.OptionalBoolTrue)} 340 } 341 342 options := new(images.SearchOptions) 343 options.WithAuthfile(opts.Authfile).WithFilters(mappedFilters).WithLimit(opts.Limit) 344 options.WithListTags(opts.ListTags) 345 if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { 346 if s == types.OptionalBoolTrue { 347 options.WithSkipTLSVerify(true) 348 } else { 349 options.WithSkipTLSVerify(false) 350 } 351 } 352 return images.Search(ir.ClientCtx, term, options) 353 } 354 355 func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { 356 return config.Default() 357 } 358 359 func (ir *ImageEngine) Build(_ context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) { 360 report, err := images.Build(ir.ClientCtx, containerFiles, opts) 361 if err != nil { 362 return nil, err 363 } 364 return report, nil 365 } 366 367 func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { 368 options := new(images.TreeOptions).WithWhatRequires(opts.WhatRequires) 369 return images.Tree(ir.ClientCtx, nameOrID, options) 370 } 371 372 // Shutdown Libpod engine 373 func (ir *ImageEngine) Shutdown(_ context.Context) { 374 } 375 376 func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) { 377 return nil, errors.New("not implemented yet") 378 }