1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
package image
import (
"fmt"
"io"
"os"
"github.com/containers/image/docker/reference"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/libpod"
"github.com/projectatomic/libpod/pkg/inspect"
)
// Image is the primary struct for dealing with images
// It is still very much a work in progress
type Image struct {
inspect.ImageData
InputName string
Local bool
runtime *libpod.Runtime
image *storage.Image
}
// NewFromLocal creates a new image object that is intended
// to only deal with local images already in the store (or
// its aliases)
func NewFromLocal(name string, runtime *libpod.Runtime) (Image, error) {
image := Image{
InputName: name,
Local: true,
runtime: runtime,
}
localImage, err := image.getLocalImage()
if err != nil {
return Image{}, err
}
image.image = localImage
return image, nil
}
// New creates a new image object where the image could be local
// or remote
func New(name string, runtime *libpod.Runtime) (Image, error) {
// We don't know if the image is local or not ... check local first
newImage := Image{
InputName: name,
Local: false,
runtime: runtime,
}
localImage, err := newImage.getLocalImage()
if err == nil {
newImage.Local = true
newImage.image = localImage
return newImage, nil
}
// The image is not local
pullNames, err := newImage.createNamesToPull()
if err != nil {
return newImage, err
}
if len(pullNames) == 0 {
return newImage, errors.Errorf("unable to pull %s", newImage.InputName)
}
var writer io.Writer
writer = os.Stderr
for _, p := range pullNames {
_, err := newImage.pull(p, writer, runtime)
if err == nil {
newImage.InputName = p
img, err := newImage.getLocalImage()
newImage.image = img
return newImage, err
}
}
return newImage, errors.Errorf("unable to find %s", name)
}
// getLocalImage resolves an unknown input describing an image and
// returns a storage.Image or an error. It is used by NewFromLocal.
func (i *Image) getLocalImage() (*storage.Image, error) {
imageError := fmt.Sprintf("unable to find '%s' in local storage\n", i.InputName)
if i.InputName == "" {
return nil, errors.Errorf("input name is blank")
}
var taggedName string
img, err := i.runtime.GetImage(i.InputName)
if err == nil {
return img, err
}
// container-storage wasn't able to find it in its current form
// check if the input name has a tag, and if not, run it through
// again
decomposedImage, err := decompose(i.InputName)
if err != nil {
return nil, err
}
// the inputname isn't tagged, so we assume latest and try again
if !decomposedImage.isTagged {
taggedName = fmt.Sprintf("%s:latest", i.InputName)
img, err = i.runtime.GetImage(taggedName)
if err == nil {
return img, nil
}
}
hasReg, err := i.hasRegistry()
if err != nil {
return nil, errors.Wrapf(err, imageError)
}
// if the input name has a registry in it, the image isnt here
if hasReg {
return nil, errors.Errorf("%s", imageError)
}
// grab all the local images
images, err := i.runtime.GetImages(&libpod.ImageFilterParams{})
if err != nil {
return nil, err
}
// check the repotags of all images for a match
repoImage, err := findImageInRepotags(decomposedImage, images)
if err == nil {
return repoImage, nil
}
return nil, errors.Errorf("%s", imageError)
}
// hasRegistry returns a bool/err response if the image has a registry in its
// name
func (i *Image) hasRegistry() (bool, error) {
imgRef, err := reference.Parse(i.InputName)
if err != nil {
return false, err
}
registry := reference.Domain(imgRef.(reference.Named))
if registry != "" {
return true, nil
}
return false, nil
}
// ID returns the image ID as a string
func (i *Image) ID() string {
return i.image.ID
}
// createNamesToPull looks at a decomposed image and determines the possible
// images names to try pulling in combination with the registries.conf file as well
func (i *Image) createNamesToPull() ([]string, error) {
var pullNames []string
decomposedImage, err := decompose(i.InputName)
if err != nil {
return nil, err
}
if decomposedImage.hasRegistry {
pullNames = append(pullNames, i.InputName)
} else {
registries, err := libpod.GetRegistries()
if err != nil {
return nil, err
}
for _, registry := range registries {
decomposedImage.registry = registry
pullNames = append(pullNames, decomposedImage.assemble())
}
}
return pullNames, nil
}
// pull is a temporary function for stage1 to be able to pull images during the image
// resolution tests. it will be replaced in stage2 with a more robust function.
func (i *Image) pull(name string, writer io.Writer, r *libpod.Runtime) (string, error) {
options := libpod.CopyOptions{
Writer: writer,
SignaturePolicyPath: r.GetConfig().SignaturePolicyPath,
}
return i.runtime.PullImage(name, options)
}
// Remove an image
// This function is only complete enough for the stage 1 tests.
func (i *Image) Remove(force bool) error {
_, err := i.runtime.RemoveImage(i.image, force)
return err
}
|