diff --git a/image/image.go b/image/image.go index 1551c1c..17532ce 100644 --- a/image/image.go +++ b/image/image.go @@ -87,6 +87,14 @@ func validate(w walker, refs []string, out *log.Logger) error { if err := m.validate(w); err != nil { return err } + + ml, err := findManifest_list(w, d) + if err == nil { + if err := ml.validate(w); err != nil { + return err + } + } + if out != nil { out.Printf("reference %q: OK", ref) } diff --git a/image/manifest_list.go b/image/manifest_list.go new file mode 100644 index 0000000..031cfd1 --- /dev/null +++ b/image/manifest_list.go @@ -0,0 +1,111 @@ +// Copyright 2016 The Linux Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package image + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/opencontainers/image-spec/schema" + Descriptor "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type manifest_list struct { + Manifests []ManifestDescriptor `json:"manifests"` +} + +type ManifestDescriptor struct { + Descriptor + + // Platform describes the platform which the image in the manifest runs on. + Platform Platform `json:"platform"` +} + +func findManifest_list(w walker, d *descriptor) (*manifest_list, error) { + var ml manifest_list + mlpath := filepath.Join("blobs", d.algo(), d.hash()) + + switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error { + if info.IsDir() || filepath.Clean(path) != mlpath { + return nil + } + + buf, err := ioutil.ReadAll(r) + if err != nil { + return errors.Wrapf(err, "%s: error reading manifest_list", path) + } + + if err := schema.MediaTypeManifestList.Validate(bytes.NewReader(buf)); err != nil { + return errors.Wrapf(err, "%s: manifest_list validation failed", path) + } + + if err := json.Unmarshal(buf, &ml); err != nil { + return err + } + + if len(m.Manifests) == 0 { + return fmt.Errorf("%s: no manifests found", path) + } + + return errEOW + }); err { + case nil: + return nil, fmt.Errorf("%s: manifest_list not found", mpath) + case errEOW: + return &m, nil + default: + return nil, err + } +} + +func (ml *manifest_list) validate(w walker) error { + for _, d := range ml.Manifests { + if err := d.Descriptor.validate(w, []string{v1.MediaTypeImageConfig}); err != nil { + return errors.Wrap(err, "Descriptor validation failed") + } + if err := checkPlatform(d); err != nil { + return errors.Wrap(err, "platform validation failed") + } + } + + return nil +} + +func checkPlatform(m Manifests) error { + validCombins := map[string][]string{ + "darwin": {"386", "amd64", "arm", "arm64"}, + "dragonfly": {"amd64"}, + "freebsd": {"386", "amd64", "arm"}, + "linux": {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le"}, + "netbsd": {"386", "amd64", "arm"}, + "openbsd": {"386", "amd64", "arm"}, + "plan9": {"386", "amd64"}, + "solaris": {"amd64"}, + "windows": {"386", "amd64"}} + for os, archs := range validCombins { + if os == m.Platform.OS { + for _, arch := range archs { + if arch == m.Platform.Architecture { + return nil + } + } + return fmt.Errorf("Combination of %q and %q is invalid.", m.Platform.OS, m.Platform.Architecture) + } + } + return fmt.Errorf("Operation system %q of the bundle is not supported yet.", m.Platform.OS) +}