Skip to content

Commit c8e4b53

Browse files
committed
image/refs: Add a generic name-based reference interface
And implement that interface for tarballs based on the specs image-layout. I plan on adding other backends later, but this is enough for a proof of concept. Also add new 'refs list' and 'refs get' subcommands to oci-image-tool so folks can access the new read functionality from the command line. In a subsequent commit, I'll replace oci-image-tools walker functionality with this new API. Signed-off-by: W. Trevor King <[email protected]>
1 parent 09f52ba commit c8e4b53

File tree

7 files changed

+426
-0
lines changed

7 files changed

+426
-0
lines changed

cmd/oci-image-tool/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func main() {
3333
cmd.AddCommand(newValidateCmd(stdout, stderr))
3434
cmd.AddCommand(newUnpackCmd(stdout, stderr))
3535
cmd.AddCommand(newBundleCmd(stdout, stderr))
36+
cmd.AddCommand(newRefsCmd(os.Stdout, stderr))
3637
cmd.AddCommand(newCASCmd(os.Stdout, stderr))
3738

3839
if err := cmd.Execute(); err != nil {

cmd/oci-image-tool/refs.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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 main
16+
17+
import (
18+
"io"
19+
"log"
20+
21+
"github.com/spf13/cobra"
22+
)
23+
24+
func newRefsCmd(stdout io.Writer, stderr *log.Logger) *cobra.Command {
25+
cmd := &cobra.Command{
26+
Use: "refs",
27+
Short: "Name-based reference manipulation",
28+
}
29+
30+
cmd.AddCommand(newRefsGetCmd(stdout, stderr))
31+
cmd.AddCommand(newRefsListCmd(stdout, stderr))
32+
33+
return cmd
34+
}

cmd/oci-image-tool/refs_get.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 main
16+
17+
import (
18+
"encoding/json"
19+
"io"
20+
"log"
21+
"os"
22+
23+
"github.com/opencontainers/image-spec/image/refs/layout"
24+
"github.com/spf13/cobra"
25+
)
26+
27+
type refsGetCmd struct {
28+
stdout io.Writer
29+
stderr *log.Logger
30+
path string
31+
name string
32+
}
33+
34+
func newRefsGetCmd(stdout io.Writer, stderr *log.Logger) *cobra.Command {
35+
state := &refsGetCmd{
36+
stdout: stdout,
37+
stderr: stderr,
38+
}
39+
40+
return &cobra.Command{
41+
Use: "get PATH NAME",
42+
Short: "Retrieve a reference from the store",
43+
Run: state.Run,
44+
}
45+
}
46+
47+
func (state *refsGetCmd) Run(cmd *cobra.Command, args []string) {
48+
if len(args) != 2 {
49+
state.stderr.Print("both PATH and NAME must be provided")
50+
if err := cmd.Usage(); err != nil {
51+
state.stderr.Println(err)
52+
}
53+
os.Exit(1)
54+
}
55+
56+
state.path = args[0]
57+
state.name = args[1]
58+
59+
err := state.run()
60+
if err != nil {
61+
state.stderr.Println(err)
62+
os.Exit(1)
63+
}
64+
65+
os.Exit(0)
66+
}
67+
68+
func (state *refsGetCmd) run() (err error) {
69+
engine, err := layout.NewEngine(state.path)
70+
if err != nil {
71+
return err
72+
}
73+
defer engine.Close()
74+
75+
descriptor, err := engine.Get(state.name)
76+
if err != nil {
77+
return err
78+
}
79+
80+
return json.NewEncoder(state.stdout).Encode(&descriptor)
81+
}

cmd/oci-image-tool/refs_list.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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 main
16+
17+
import (
18+
"fmt"
19+
"io"
20+
"log"
21+
"os"
22+
23+
"github.com/opencontainers/image-spec/image/refs/layout"
24+
"github.com/spf13/cobra"
25+
)
26+
27+
type refsListCmd struct {
28+
stdout io.Writer
29+
stderr *log.Logger
30+
path string
31+
}
32+
33+
func newRefsListCmd(stdout io.Writer, stderr *log.Logger) *cobra.Command {
34+
state := &refsListCmd{
35+
stdout: stdout,
36+
stderr: stderr,
37+
}
38+
39+
return &cobra.Command{
40+
Use: "list PATH",
41+
Short: "Return available names from the store.",
42+
Run: state.Run,
43+
}
44+
}
45+
46+
func (state *refsListCmd) Run(cmd *cobra.Command, args []string) {
47+
if len(args) != 1 {
48+
state.stderr.Print("PATH must be provided")
49+
if err := cmd.Usage(); err != nil {
50+
state.stderr.Println(err)
51+
}
52+
os.Exit(1)
53+
}
54+
55+
state.path = args[0]
56+
57+
err := state.run()
58+
if err != nil {
59+
state.stderr.Println(err)
60+
os.Exit(1)
61+
}
62+
63+
os.Exit(0)
64+
}
65+
66+
func (state *refsListCmd) run() (err error) {
67+
engine, err := layout.NewEngine(state.path)
68+
if err != nil {
69+
return err
70+
}
71+
defer engine.Close()
72+
73+
names, err := engine.List("", -1, 0)
74+
if err != nil {
75+
return err
76+
}
77+
78+
for _, name := range names {
79+
n, err := io.WriteString(state.stdout, fmt.Sprintf("%s\n", name))
80+
if err != nil {
81+
return err
82+
}
83+
if n < len(name)+1 {
84+
return fmt.Errorf("wrote %d of %d characters", n, len(name)+1)
85+
}
86+
}
87+
88+
return nil
89+
}

image/refs/interface.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 refs implements generic name-based reference access.
16+
package refs
17+
18+
import (
19+
"github.com/opencontainers/image-spec/image/structure"
20+
)
21+
22+
// Engine represents a name-based reference storage engine.
23+
type Engine interface {
24+
25+
// Put adds a new reference to the store.
26+
Put(name string, descriptor *structure.Descriptor) (err error)
27+
28+
// Get returns a reference from the store.
29+
Get(name string) (descriptor *structure.Descriptor, err error)
30+
31+
// List returns a slice of available names from the store.
32+
//
33+
// Results are sorted alphabetically.
34+
//
35+
// Arguments:
36+
//
37+
// * prefix: limits the result set to names starting with that
38+
// value.
39+
// * size: limits the length of the result set to the first 'size'
40+
// matches. A value of -1 means "all results".
41+
// * from: shifts the result set to start from the 'from'th match.
42+
//
43+
// For example, a store with names like:
44+
//
45+
// * 123
46+
// * abcd
47+
// * abce
48+
// * abcf
49+
// * abcg
50+
//
51+
// will have the following call/result pairs:
52+
//
53+
// * List("", -1, 0) -> ["123", "abcd", "abce", "abcf", "abcg"].
54+
// * List("", 2, 0) -> ["123", "abcd"]
55+
// * List("", 2, 1) -> ["abcd", "abce"]
56+
// * List("abc", 2, 1) -> ["abce", "abcf"].
57+
List(prefix string, size int, from int) (names []string, err error)
58+
59+
// Delete removes a reference from the store.
60+
Delete(name string) (err error)
61+
62+
// Close releases resources held by the engine. Subsequent engine
63+
// method calls will fail.
64+
Close() (err error)
65+
}

image/refs/layout/main.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 layout implements the refs interface using the image-spec's
16+
// image-layout [1].
17+
//
18+
// [1]: https://github.com/opencontainers/image-spec/blob/master/image-layout.md
19+
package layout
20+
21+
import (
22+
"os"
23+
24+
"github.com/opencontainers/image-spec/image/refs"
25+
)
26+
27+
// NewEngine instantiates an engine with the appropriate backend (tar,
28+
// HTTP, ...).
29+
func NewEngine(path string) (engine refs.Engine, err error) {
30+
file, err := os.Open(path)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
return NewTarEngine(file)
36+
}

0 commit comments

Comments
 (0)