Skip to content

Commit 4f0b3c1

Browse files
committed
WIP: validate: Stub in callback-based validation
1 parent 7c9efa7 commit 4f0b3c1

File tree

3 files changed

+154
-112
lines changed

3 files changed

+154
-112
lines changed

cmd/oci-image-validate/main.go

Lines changed: 41 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -16,145 +16,74 @@ package main
1616

1717
import (
1818
"fmt"
19-
"log"
2019
"os"
21-
"strings"
2220

23-
"github.com/opencontainers/image-spec/schema"
24-
"github.com/opencontainers/image-tools/image"
25-
"github.com/pkg/errors"
21+
"github.com/mndrix/tap.go"
22+
"github.com/opencontainers/image-spec/specs-go"
23+
"github.com/opencontainers/image-tools/image/cas/layout"
24+
"github.com/opencontainers/image-tools/validate"
2625
"github.com/spf13/cobra"
2726
"golang.org/x/net/context"
2827
)
2928

30-
// supported validation types
31-
var validateTypes = []string{
32-
image.TypeImage,
33-
image.TypeManifest,
34-
image.TypeManifestList,
35-
image.TypeConfig,
36-
}
37-
3829
type validateCmd struct {
39-
stdout *log.Logger
40-
stderr *log.Logger
41-
typ string // the type to validate, can be empty string
42-
refs []string
30+
mediaType string // the type to validate, can be empty string
31+
digest string
32+
strict bool
4333
}
4434

4535
func main() {
46-
stdout := log.New(os.Stdout, "", 0)
47-
stderr := log.New(os.Stderr, "", 0)
48-
49-
cmd := newValidateCmd(stdout, stderr)
50-
if err := cmd.Execute(); err != nil {
51-
stderr.Println(err)
52-
os.Exit(1)
53-
}
54-
}
55-
56-
func newValidateCmd(stdout, stderr *log.Logger) *cobra.Command {
57-
v := &validateCmd{
58-
stdout: stdout,
59-
stderr: stderr,
60-
}
36+
validator := &validateCmd{}
6137

6238
cmd := &cobra.Command{
63-
Use: "oci-image-validate FILE...",
64-
Short: "Validate one or more image files",
65-
Run: v.Run,
39+
Use: "oci-image-validate PATH DIGEST",
40+
Short: "Validate an OCI image",
41+
Run: validator.Run,
6642
}
6743

68-
cmd.Flags().StringVar(
69-
&v.typ, "type", "",
70-
fmt.Sprintf(
71-
`Type of the file to validate. If unset, oci-image-tool will try to auto-detect the type. One of "%s".`,
72-
strings.Join(validateTypes, ","),
73-
),
74-
)
75-
76-
cmd.Flags().StringSliceVar(
77-
&v.refs, "ref", nil,
78-
`A set of refs pointing to the manifests to be validated. Each reference must be present in the "refs" subdirectory of the image. Only applicable if type is image.`,
79-
)
44+
err := cmd.Execute()
45+
if err != nil {
46+
fmt.Fprintln(os.Stderr, err)
47+
os.Exit(1)
48+
}
8049

81-
return cmd
50+
os.Exit(0)
8251
}
8352

84-
func (v *validateCmd) Run(cmd *cobra.Command, args []string) {
85-
if len(args) < 1 {
86-
v.stderr.Printf("no files specified")
87-
if err := cmd.Usage(); err != nil {
88-
v.stderr.Println(err)
53+
func (validator *validateCmd) Run(cmd *cobra.Command, args []string) {
54+
if len(args) != 2 {
55+
err := cmd.Usage()
56+
if err != nil {
57+
fmt.Fprintln(os.Stderr, err)
8958
}
9059
os.Exit(1)
9160
}
9261

93-
ctx := context.Background()
94-
95-
var exitcode int
96-
for _, arg := range args {
97-
err := v.validatePath(ctx, arg)
98-
99-
if err == nil {
100-
v.stdout.Printf("%s: OK", arg)
101-
continue
102-
}
103-
104-
var errs []error
105-
if verr, ok := errors.Cause(err).(schema.ValidationError); ok {
106-
errs = verr.Errs
107-
} else if serr, ok := errors.Cause(err).(*schema.SyntaxError); ok {
108-
v.stderr.Printf("%s:%d:%d: validation failed: %v", arg, serr.Line, serr.Col, err)
109-
exitcode = 1
110-
continue
111-
} else {
112-
v.stderr.Printf("%s: validation failed: %v", arg, err)
113-
exitcode = 1
114-
continue
115-
}
62+
path := args[0]
63+
validator.mediaType = "application/vnd.oci.image.layer.tar+gzip"
64+
validator.digest = args[1]
65+
validator.strict = true
11666

117-
for _, err := range errs {
118-
v.stderr.Printf("%s: validation failed: %v", arg, err)
119-
}
67+
ctx := context.Background()
12068

121-
exitcode = 1
69+
engine, err := layout.NewEngine(ctx, path)
70+
if err != nil {
71+
fmt.Fprintln(os.Stderr, err)
72+
os.Exit(1)
12273
}
74+
defer engine.Close()
12375

124-
os.Exit(exitcode)
125-
}
76+
harness := tap.New()
77+
harness.Header(0)
12678

127-
func (v *validateCmd) validatePath(ctx context.Context, name string) error {
128-
var (
129-
err error
130-
typ = v.typ
131-
)
132-
133-
if typ == "" {
134-
if typ, err = image.Autodetect(name); err != nil {
135-
return errors.Wrap(err, "unable to determine type")
136-
}
137-
}
138-
139-
switch typ {
140-
case image.TypeImage:
141-
return image.Validate(ctx, name, v.refs, v.stdout)
79+
descriptor := &specs.Descriptor{
80+
MediaType: validator.mediaType,
81+
Digest: validator.digest,
82+
Size: -1,
14283
}
84+
validate.Validate(ctx, harness, engine, descriptor, validator.strict)
14385

144-
f, err := os.Open(name)
145-
if err != nil {
146-
return errors.Wrap(err, "unable to open file")
147-
}
148-
defer f.Close()
149-
150-
switch typ {
151-
case image.TypeManifest:
152-
return schema.MediaTypeManifest.Validate(f)
153-
case image.TypeManifestList:
154-
return schema.MediaTypeManifestList.Validate(f)
155-
case image.TypeConfig:
156-
return schema.MediaTypeImageConfig.Validate(f)
157-
}
86+
harness.AutoPlan()
15887

159-
return fmt.Errorf("type %q unimplemented", typ)
88+
return
16089
}

validate/layer.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2016 The Linux Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package validate provides a framework for validating OCI images.
16+
package validate
17+
18+
import (
19+
"archive/tar"
20+
"compress/gzip"
21+
"fmt"
22+
"io"
23+
24+
"github.com/mndrix/tap.go"
25+
"github.com/opencontainers/image-tools/image/cas"
26+
"golang.org/x/net/context"
27+
)
28+
29+
func ValidateGzippedLayer(ctx context.Context, harness *tap.T, engine cas.Engine, digest string, strict bool) {
30+
reader, err := engine.Get(ctx, digest)
31+
harness.Ok(err == nil, fmt.Sprintf("retrieve %s from CAS", digest))
32+
if err != nil {
33+
return
34+
}
35+
36+
gzipReader, err := gzip.NewReader(reader)
37+
harness.Ok(err == nil, fmt.Sprintf("gzip reader for %s", digest))
38+
if err != nil {
39+
return
40+
}
41+
42+
tarReader := tar.NewReader(gzipReader)
43+
for {
44+
select {
45+
case <-ctx.Done():
46+
harness.Ok(false, ctx.Err().Error())
47+
return
48+
default:
49+
}
50+
51+
header, err := tarReader.Next()
52+
if err == io.EOF {
53+
return
54+
}
55+
harness.Ok(err == nil, fmt.Sprintf("read tar header from %s", digest))
56+
if err != nil {
57+
return
58+
}
59+
60+
message := fmt.Sprintf("ustar typeflag in %s (%q)", digest, header.Typeflag)
61+
if header.Typeflag < tar.TypeReg || header.Typeflag > tar.TypeFifo {
62+
harness.Ok(!strict, message)
63+
} else {
64+
harness.Ok(true, message)
65+
}
66+
}
67+
68+
return
69+
}

validate/validate.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2016 The Linux Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package validate provides a framework for validating OCI images.
16+
package validate
17+
18+
import (
19+
"fmt"
20+
21+
"github.com/mndrix/tap.go"
22+
"github.com/opencontainers/image-spec/specs-go"
23+
"github.com/opencontainers/image-tools/image/cas"
24+
"golang.org/x/net/context"
25+
)
26+
27+
// Validate is a template for validating a CAS object.
28+
type Validator func(ctx context.Context, harness *tap.T, engine cas.Engine, digest string, strict bool)
29+
30+
// Validators is a map from media types to an appropriate Validator function.
31+
var Validators = map[string]Validator{
32+
"application/vnd.oci.image.layer.tar+gzip": ValidateGzippedLayer,
33+
"application/vnd.oci.image.layer.nondistributable.tar+gzip": ValidateGzippedLayer,
34+
// TODO: fill in other types
35+
}
36+
37+
func Validate(ctx context.Context, harness *tap.T, engine cas.Engine, descriptor *specs.Descriptor, strict bool) {
38+
validator, ok := Validators[descriptor.MediaType]
39+
harness.Ok(ok, fmt.Sprintf("recognized media type %q", descriptor.MediaType))
40+
if !ok {
41+
return
42+
}
43+
validator(ctx, harness, engine, descriptor.Digest, strict)
44+
}

0 commit comments

Comments
 (0)