
The split between provider and ingester was a long standing division reflecting the client-side use cases. For the most part, we were differentiating these for the algorithms that operate them, but it made instantation and use of the types challenging. On the server-side, this distinction is generally less important. This change unifies these types and in the process we get a few benefits. The first is that we now completely access the content store over GRPC. This was the initial intent and we have now satisfied this goal completely. There are a few issues around listing content and getting status, but we resolve these with simple streaming and regexp filters. More can probably be done to polish this but the result is clean. Several other content-oriented methods were polished in the process of unification. We have now properly seperated out the `Abort` method to cancel ongoing or stalled ingest processes. We have also replaced the `Active` method with a single status method. The transition went extremely smoothly. Once the clients were updated to use the new methods, every thing worked as expected on the first compile. Signed-off-by: Stephen J Day <stephen.day@docker.com>
154 lines
3.7 KiB
Go
154 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containerd/console"
|
|
contentapi "github.com/containerd/containerd/api/services/content"
|
|
imagesapi "github.com/containerd/containerd/api/services/images"
|
|
"github.com/containerd/containerd/content"
|
|
"github.com/containerd/containerd/images"
|
|
"github.com/containerd/containerd/remotes"
|
|
"github.com/containerd/containerd/remotes/docker"
|
|
contentservice "github.com/containerd/containerd/services/content"
|
|
imagesservice "github.com/containerd/containerd/services/images"
|
|
"github.com/pkg/errors"
|
|
"github.com/urfave/cli"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
var registryFlags = []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "skip-verify,k",
|
|
Usage: "Skip SSL certificate validation",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "plain-http",
|
|
Usage: "Allow connections using plain HTTP",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "user,u",
|
|
Usage: "user[:password] Registry user and password",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "refresh",
|
|
Usage: "Refresh token for authorization server",
|
|
},
|
|
}
|
|
|
|
func resolveContentStore(context *cli.Context) (content.Store, error) {
|
|
root := filepath.Join(context.GlobalString("root"), "content")
|
|
if !filepath.IsAbs(root) {
|
|
var err error
|
|
root, err = filepath.Abs(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
conn, err := connectGRPC(context)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return contentservice.NewStoreFromClient(contentapi.NewContentClient(conn)), nil
|
|
}
|
|
|
|
func resolveImageStore(clicontext *cli.Context) (images.Store, error) {
|
|
conn, err := connectGRPC(clicontext)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return imagesservice.NewStoreFromClient(imagesapi.NewImagesClient(conn)), nil
|
|
}
|
|
|
|
func connectGRPC(context *cli.Context) (*grpc.ClientConn, error) {
|
|
address := context.GlobalString("address")
|
|
timeout := context.GlobalDuration("connect-timeout")
|
|
return grpc.Dial(address,
|
|
grpc.WithTimeout(timeout),
|
|
grpc.WithBlock(),
|
|
grpc.WithInsecure(),
|
|
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
|
|
return net.DialTimeout("unix", address, timeout)
|
|
}),
|
|
)
|
|
}
|
|
|
|
// getResolver prepares the resolver from the environment and options.
|
|
func getResolver(ctx context.Context, clicontext *cli.Context) (remotes.Resolver, error) {
|
|
username := clicontext.String("user")
|
|
var secret string
|
|
if i := strings.IndexByte(username, ':'); i > 0 {
|
|
secret = username[i+1:]
|
|
username = username[0:i]
|
|
}
|
|
options := docker.ResolverOptions{
|
|
PlainHTTP: clicontext.Bool("plain-http"),
|
|
}
|
|
if username != "" {
|
|
if secret == "" {
|
|
fmt.Printf("Password: ")
|
|
|
|
var err error
|
|
secret, err = passwordPrompt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fmt.Print("\n")
|
|
}
|
|
} else if rt := clicontext.String("refresh"); rt != "" {
|
|
secret = rt
|
|
}
|
|
|
|
options.Credentials = func(host string) (string, string, error) {
|
|
// Only one host
|
|
return username, secret, nil
|
|
}
|
|
|
|
tr := &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DialContext: (&net.Dialer{
|
|
Timeout: 30 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
DualStack: true,
|
|
}).DialContext,
|
|
MaxIdleConns: 10,
|
|
IdleConnTimeout: 30 * time.Second,
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
TLSClientConfig: &tls.Config{
|
|
InsecureSkipVerify: clicontext.Bool("insecure"),
|
|
},
|
|
ExpectContinueTimeout: 5 * time.Second,
|
|
}
|
|
|
|
options.Client = &http.Client{
|
|
Transport: tr,
|
|
}
|
|
|
|
return docker.NewResolver(options), nil
|
|
}
|
|
|
|
func passwordPrompt() (string, error) {
|
|
c := console.Current()
|
|
defer c.Reset()
|
|
|
|
if err := c.DisableEcho(); err != nil {
|
|
return "", errors.Wrap(err, "failed to disable echo")
|
|
}
|
|
|
|
line, _, err := bufio.NewReader(c).ReadLine()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed to read line")
|
|
}
|
|
return string(line), nil
|
|
}
|