Remove namespace & repo opts form push and bump v2

- As a result of removing namespace from push command, the repo
was also suppressed.
- Bump of the metadata schema do v0.2 accepting any formatting
on `name` and removing `namespace`

Signed-off-by: Ulysses Souza <ulysses.souza@docker.com>
This commit is contained in:
Ulysses Souza 2019-02-11 17:01:24 +01:00
Родитель 245b591a8d
Коммит 6688c3b286
17 изменённых файлов: 186 добавлений и 95 удалений

Просмотреть файл

@ -21,8 +21,7 @@ import (
)
type bundleOptions struct {
invocationImageName string
out string
out string
}
func bundleCmd(dockerCli command.Cli) *cobra.Command {
@ -36,13 +35,12 @@ func bundleCmd(dockerCli command.Cli) *cobra.Command {
},
}
cmd.Flags().StringVarP(&opts.invocationImageName, "invocation-image", "i", "", "specify the name of invocation image to build")
cmd.Flags().StringVarP(&opts.out, "out", "o", "bundle.json", "path to the output bundle.json (- for stdout)")
return cmd
}
func runBundle(dockerCli command.Cli, appName string, opts bundleOptions) error {
bundle, err := makeBundle(dockerCli, appName, opts.invocationImageName)
bundle, err := makeBundle(dockerCli, appName)
if err != nil {
return err
}
@ -61,22 +59,22 @@ func runBundle(dockerCli command.Cli, appName string, opts bundleOptions) error
return ioutil.WriteFile(opts.out, bundleBytes, 0644)
}
func makeBundle(dockerCli command.Cli, appName, invocationImageName string) (*bundle.Bundle, error) {
func makeBundle(dockerCli command.Cli, appName string) (*bundle.Bundle, error) {
app, err := packager.Extract(appName)
if err != nil {
return nil, err
}
defer app.Cleanup()
return makeBundleFromApp(dockerCli, app, invocationImageName)
return makeBundleFromApp(dockerCli, app)
}
func makeBundleFromApp(dockerCli command.Cli, app *types.App, invocationImageName string) (*bundle.Bundle, error) {
func makeBundleFromApp(dockerCli command.Cli, app *types.App) (*bundle.Bundle, error) {
meta := app.Metadata()
invocationImageName, err := makeImageName(meta, invocationImageName, "-invoc")
invocationImageName, err := makeImageName(meta)
if err != nil {
return nil, err
}
if _, err := makeImageName(app.Metadata(), "", ""); err != nil {
if _, err := makeImageName(meta); err != nil {
return nil, err
}
@ -100,12 +98,10 @@ func makeBundleFromApp(dockerCli command.Cli, app *types.App, invocationImageNam
return packager.ToCNAB(app, invocationImageName)
}
func makeImageName(meta metadata.AppMetadata, name, suffix string) (string, error) {
if name == "" {
name = fmt.Sprintf("%s:%s%s", meta.Name, meta.Version, suffix)
}
func makeImageName(meta metadata.AppMetadata) (string, error) {
name := fmt.Sprintf("%s:%s-invoc", meta.Name, meta.Version)
if _, err := reference.ParseNormalizedNamed(name); err != nil {
return "", errors.Wrapf(err, "image name %q is invalid, please check namespace, name and version fields", name)
return "", errors.Wrapf(err, "image name %q is invalid, please check name and version fields", name)
}
return name, nil
}

Просмотреть файл

@ -9,42 +9,16 @@ import (
func TestMakeInvocationImage(t *testing.T) {
testcases := []struct {
name string
imageName string
meta metadata.AppMetadata
expected string
err string
name string
meta metadata.AppMetadata
expected string
err string
}{
{
name: "specify-image-name",
imageName: "my-invocation-image",
expected: "my-invocation-image",
},
{
name: "specify-image-name-and-namespace",
imageName: "my-invocation-image",
expected: "my-invocation-image",
},
{
name: "simple-metadata",
meta: metadata.AppMetadata{Name: "name", Version: "version"},
expected: "name:version-invoc",
},
{
name: "simple-metadata-with-overridden-namespace",
meta: metadata.AppMetadata{Name: "name", Version: "version"},
expected: "name:version-invoc",
},
{
name: "metadata-with-namespace",
meta: metadata.AppMetadata{Name: "name", Version: "version"},
expected: "name:version-invoc",
},
{
name: "metadata-with-namespace-and-overridden-namespace",
meta: metadata.AppMetadata{Name: "name", Version: "version"},
expected: "name:version-invoc",
},
{
name: "simple-metadata",
meta: metadata.AppMetadata{Name: "WrongName&%*", Version: "version"},
@ -53,10 +27,10 @@ func TestMakeInvocationImage(t *testing.T) {
}
for _, c := range testcases {
t.Run(c.name, func(t *testing.T) {
actual, err := makeImageName(c.meta, c.imageName, "-invoc")
actual, err := makeImageName(c.meta)
if c.err != "" {
assert.ErrorContains(t, err, c.err)
assert.Equal(t, actual, "")
assert.Equal(t, actual, "", "On "+c.meta.Name)
} else {
assert.NilError(t, err)
assert.Equal(t, actual, c.expected)

Просмотреть файл

@ -123,7 +123,7 @@ func extractAndLoadAppBasedBundle(dockerCli command.Cli, name string) (*bundle.B
return nil, err
}
defer app.Cleanup()
return makeBundleFromApp(dockerCli, app, "")
return makeBundleFromApp(dockerCli, app)
}
func resolveBundle(dockerCli command.Cli, name string) (*bundle.Bundle, error) {

Просмотреть файл

@ -27,7 +27,7 @@ func inspectCmd(dockerCli command.Cli) *cobra.Command {
if err != nil {
return err
}
bundle, err := resolveBundle(dockerCli, "", appname)
bundle, err := resolveBundle(dockerCli, appname)
if err != nil {
return err
}

Просмотреть файл

@ -9,9 +9,7 @@ import (
)
type pushOptions struct {
namespace string
tag string
repo string
tag string
}
func pushCmd() *cobra.Command {
@ -26,15 +24,13 @@ func pushCmd() *cobra.Command {
return err
}
defer app.Cleanup()
dgst, err := packager.Push(app, opts.namespace, opts.tag, opts.repo)
dgst, err := packager.Push(app, opts.tag)
if err == nil {
fmt.Println(dgst)
}
return err
},
}
cmd.Flags().StringVar(&opts.namespace, "namespace", "", "Namespace to use")
cmd.Flags().StringVarP(&opts.tag, "tag", "t", "", "Tag to use (default: version in metadata)")
cmd.Flags().StringVar(&opts.repo, "repo", "", "Name of the remote repository (default: <app-name>.dockerapp)")
cmd.Flags().StringVarP(&opts.tag, "tag", "t", "", "Target registry reference (default is : from metadata)")
return cmd
}

Просмотреть файл

@ -241,10 +241,10 @@ func TestBundle(t *testing.T) {
// List the images on the build context daemon and checks the invocation image is there
cmd.Command = []string{dockerCli, "image", "ls", "--format", "{{.Repository}}:{{.Tag}}"}
icmd.RunCmd(cmd).Assert(t, icmd.Expected{ExitCode: 0, Out: "acmecorp/simple:1.1.0-beta1-invoc"})
icmd.RunCmd(cmd).Assert(t, icmd.Expected{ExitCode: 0, Out: "simple:1.1.0-beta1-invoc"})
// Copy all the files from the invocation image and check them
cmd.Command = []string{dockerCli, "create", "--name", "invocation", "acmecorp/simple:1.1.0-beta1-invoc"}
cmd.Command = []string{dockerCli, "create", "--name", "invocation", "simple:1.1.0-beta1-invoc"}
id := strings.TrimSpace(icmd.RunCmd(cmd).Assert(t, icmd.Success).Stdout())
cmd.Command = []string{dockerCli, "cp", "invocation:/cnab/app/simple.dockerapp", tmpDir.Join("simple.dockerapp")}
icmd.RunCmd(cmd).Assert(t, icmd.Success)

Просмотреть файл

@ -1,7 +1,6 @@
version: 0.1.0
name: myapp
description: ""
namespace: "alice"
maintainers:
- name: bearclaw
email: bearclaw@bearclaw.bearclaw

4
e2e/testdata/simple-bundle.json.golden поставляемый
Просмотреть файл

@ -1,5 +1,5 @@
{
"name": "acmecorp/simple",
"name": "simple",
"version": "1.1.0-beta1",
"description": "new fancy webapp with microservices",
"maintainers": [
@ -17,7 +17,7 @@
"invocationImages": [
{
"imageType": "docker",
"image": "acmecorp/simple:1.1.0-beta1-invoc"
"image": "simple:1.1.0-beta1-invoc"
}
],
"images": {

Просмотреть файл

@ -1,6 +1,5 @@
version: 1.1.0-beta1
name: simple
namespace: acmecorp
description: "new fancy webapp with microservices"
maintainers:
- name: John Developer

Просмотреть файл

@ -4,8 +4,6 @@ version: 0.1.0
name: voting-app
# A short description of the application
description: "Dogs or cats?"
# Namespace to use when pushing to a registry. This is typically your Hub username.
namespace: myhubusername
# List of application maintainers with name and email for each
maintainers:
- name: user

Просмотреть файл

@ -4,5 +4,5 @@ var (
// Experimental enables experimental features if set to "on"
Experimental = "on"
// MetadataVersion defines the current schema version
MetadataVersion = "v0.1"
MetadataVersion = "v0.2"
)

Просмотреть файл

@ -85,26 +85,20 @@ func ExtractImagePayloadToDiskFiles(appDir string, payload map[string]string) er
}
// Push pushes an app to a registry. Returns the image digest.
func Push(app *types.App, namespace, tag, repo string) (string, error) {
func Push(app *types.App, tag string) (string, error) {
payload, err := createPayload(app)
if err != nil {
return "", errors.Wrap(err, "failed to read external file while creating payload for push")
}
imageName := createImageName(app, namespace, tag, repo)
imageName := createImageName(app, tag)
return resto.PushConfigMulti(context.Background(), payload, imageName, resto.RegistryOptions{}, nil)
}
func createImageName(app *types.App, namespace, tag, repo string) string {
if tag == "" {
tag = app.Metadata().Version
func createImageName(app *types.App, registryReference string) string {
if registryReference != "" {
return registryReference
}
if repo == "" {
repo = internal.AppNameFromDir(app.Name) + internal.AppExtension
}
if namespace != "" && namespace[len(namespace)-1] != '/' {
namespace += "/"
}
return namespace + repo + ":" + tag
return app.Metadata().Name + ":" + app.Metadata().Version
}
func createPayload(app *types.App) (map[string]string, error) {

Просмотреть файл

@ -1,6 +1,5 @@
version: 0.1.0
name: packing
namespace: my-namespace
description: "hello"
maintainers:
- name: bearclaw

Просмотреть файл

@ -6,6 +6,8 @@ import (
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
@ -100,7 +102,24 @@ func (f *_escFile) Close() error {
}
func (f *_escFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil
if !f.isDir {
return nil, fmt.Errorf(" escFile.Readdir: '%s' is not directory", f.name)
}
fis, ok := _escDirs[f.local]
if !ok {
return nil, fmt.Errorf(" escFile.Readdir: '%s' is directory, but we have no info about content of this dir, local=%s", f.name, f.local)
}
limit := count
if count <= 0 || limit > len(fis) {
limit = len(fis)
}
if len(fis) == 0 && count > 0 {
return nil, io.EOF
}
return []os.FileInfo(fis[0:limit]), nil
}
func (f *_escFile) Stat() (os.FileInfo, error) {
@ -191,25 +210,48 @@ func _escFSMustString(useLocal bool, name string) string {
var _escData = map[string]*_escFile{
"/schemas/metadata_schema_v0.1.json": {
name: "metadata_schema_v0.1.json",
local: "schemas/metadata_schema_v0.1.json",
size: 1019,
size: 1916,
modtime: 1518458244,
compressed: `
H4sIAAAAAAAC/5xSy27CQAy85ytW2x6BUKmn/AqqkJsYWJR91GuQUJV/rxLz2nYTUHNKxvZkxuPvQiml
9Gusd2hBV0rvmENVlvvo3VzQhadt2RBseL58LwV70TOZNE0/ZJGhAYa1VNfH5eJt0VNc2vgUsG/0n3us
+YIG8gGJDUZdKZEy4A4sJkjCEZmM2545rtWNJws8OPCRB4ZrQ3fr1UekaLx7SJ8dbjDWZAJPEawSdKhk
FYvTQ9vqBP7I/rj3EwPU+D/dFoxjMA4pjhMAEZx+r9Uw2r8zU+ml/nJJPiX//ulmeXa0YNqn6FejHdMB
PQgrCW58+u44RXLeZTGN3L7k7bwVTfh1MIRNYlM2n7n7Qo6sK34CAAD//7xaWqb7AwAA
H4sIAAAAAAAC/7RUwY7iMAy99yuqsEegrLQnfgUh5G1dCKJJxjFIaMS/j9pAaaZpWoG4OvbLs/38vpM0
TVPxx+YHrECsU3FgNussO1qtFi661LTPCoKSF6t/mYvNxNxVyqIuqpChAIade91dVsu/yxrikcZXg3Wi
/n/EnB9RQ9ogsUQr1qmj0sQVVOhFPAzLJNX+jtG+lpoq4KYDbblBaBNuz1xxQbJSq1H4YHGBNidpOAaw
8aLNS5Cx6/R8OgkvvA1+XPdjDeT4Gu8KpGKQCskOAwARXH+PVTJW/RonGsKyrptlBZZSyXoqNnt+5fd1
CxIzQKj446TcN4OEkg4tQfh1loSFt0onyYCMmsj2Xtr50hd0Zyi9Tt0FDQ5xHp6Ld0jPcYYPKn5Yo0oK
LK6twQrkaRRyE3yN30bkRtpbCVd1vMDR63cyWZrT9nXP/eQ2Rlvt215sb8OG8pYchtz1LdCYe00yjAnG
8aKrhQUVlZgzm+SW/AQAAP//m8slr3wHAAA=
`,
},
"/": {
isDir: true,
local: "",
"/schemas/metadata_schema_v0.2.json": {
name: "metadata_schema_v0.2.json",
local: "schemas/metadata_schema_v0.2.json",
size: 1732,
modtime: 1518458244,
compressed: `
H4sIAAAAAAAC/7RUzY7yMAy89ymq8B0LRZ/2xKsghLzUBSOSdB2DhFa8+6oNf9mmP2LFdZyxx/bE30ma
pqn65zY71KAWqdqJVIs83ztrph6dWd7mBUMp0/lH7rGJyjyTipqkUaAAgbWPrk/z2f9ZneL2TM4V1g/t
5x43ckMrthWyEDq1SL2UBjegMUCCHE6YzFbdg5fswTwhO7LmNXKBbsNUSV+CZYA2kWvKrB0xx8NBBfAq
WlgDGQEyyK5bOTDD+VcVRYK6zfE7ZSxr3iQvsCRDdVsuf5QKhV2iwipgNPJ2Ub5Mp6DkSZZi/DoSYxHs
wjsm4oMGWV2pTyVDvz0NpdWpN3jnELP4XAKfP8YZ93u/7wctHFncnYMa6DCYchmN9pu7x+R3s8dZqrSs
QepWvLx2J6OtOW5f17fv3MZgqzvrpMk4am9dd+xPZug7M6N+9ogf/uL5iW++1wv+KiSX5CcAAP//f4Kg
RsQGAAA=
`,
},
"/schemas": {
name: "schemas",
local: `schemas`,
isDir: true,
local: "schemas",
},
}
var _escDirs = map[string][]os.FileInfo{
"schemas": {
_escData["/schemas/metadata_schema_v0.1.json"],
_escData["/schemas/metadata_schema_v0.2.json"],
},
}

Просмотреть файл

@ -14,9 +14,8 @@ func TestValidateInvalidMetadata(t *testing.T) {
metadata := map[string]interface{}{
"name": "_INVALID",
}
assert.Error(t, Validate(metadata, "v0.1"),
`- name: Does not match format 'hostname'
- version: version is required`)
assert.Error(t, Validate(metadata, "v0.2"),
`- version: version is required`)
}
func TestValidateMetadata(t *testing.T) {
@ -24,5 +23,31 @@ func TestValidateMetadata(t *testing.T) {
"name": "my-name",
"version": "my-version",
}
assert.NilError(t, Validate(metadata, "v0.1"))
assert.NilError(t, Validate(metadata, "v0.2"))
}
func TestValidateMetadataNoName(t *testing.T) {
metadata := map[string]interface{}{
//"name": "my-name",
// MUST fail! No name
"version": "my-version",
}
assert.Error(t, Validate(metadata, "v0.2"), "- name: name is required")
}
func TestValidateMetadataNoVersion(t *testing.T) {
metadata := map[string]interface{}{
"name": "my-name",
//"version": "my-version",
// MUST fail! No version
}
assert.Error(t, Validate(metadata, "v0.2"), "- version: version is required")
}
func TestValidateMetadataV0_2(t *testing.T) {
metadata := map[string]interface{}{
"name": "my-name",
"version": "my-version",
}
assert.NilError(t, Validate(metadata, "v0.2"))
}

Просмотреть файл

@ -0,0 +1,71 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "metadata_schema_v0.2.json",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
},
"description": {
"type": [
"string",
"null"
]
},
"maintainers": {
"type": "array",
"items": {
"$ref": "#/definitions/maintainer"
}
},
"parents": {
"type": "array",
"items": {
"$ref": "#/definitions/parent"
}
}
},
"required": [
"name",
"version"
],
"definitions": {
"maintainer": {
"id": "#/definitions/maintainer",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"email": {
"type": [
"string",
"null"
],
"format": "email"
}
}
},
"parent": {
"id": "#/definitions/parent",
"properties": {
"name": {
"type": "string",
"format": "hostname"
},
"version": {
"type": "string"
},
"maintainers": {
"type": "array",
"items": {
"$ref": "#/definitions/maintainer"
}
}
}
}
}
}

Просмотреть файл

@ -216,8 +216,7 @@ func TestWithAttachmentsIncludingNestedCoreFiles(t *testing.T) {
func TestValidateBrokenMetadata(t *testing.T) {
r := strings.NewReader(`#version: 0.1.0-missing
name: _INVALID-name
namespace: myhubusername
name: MustBeAValidUntaggedRegistryReferenceButNotEvaluatedByTheSchema
maintainers:
- name: user
email: user@email.com
@ -229,7 +228,6 @@ unknown: property`)
err := Metadata(r)(app)
assert.Error(t, err, `failed to validate metadata:
- maintainers.2.email: Does not match format 'email'
- name: Does not match format 'hostname'
- version: version is required`)
}