@@ -26,6 +26,7 @@ import (
2626 "github.com/AkihiroSuda/nerdctl/pkg/imgutil/pull"
2727 "github.com/containerd/containerd"
2828 refdocker "github.com/containerd/containerd/reference/docker"
29+ "github.com/containerd/containerd/remotes"
2930 "github.com/containerd/stargz-snapshotter/fs/source"
3031 "github.com/pkg/errors"
3132 "github.com/sirupsen/logrus"
@@ -41,7 +42,10 @@ type EnsuredImage struct {
4142// PullMode is either one of "always", "missing", "never"
4243type PullMode = string
4344
44- func EnsureImage (ctx context.Context , client * containerd.Client , stdout io.Writer , snapshotter , rawRef string , mode PullMode ) (* EnsuredImage , error ) {
45+ // EnsureImage ensures the image.
46+ //
47+ // When insecure is set, skips verifying certs, and also falls back to HTTP when the registry does not speak HTTPS
48+ func EnsureImage (ctx context.Context , client * containerd.Client , stdout io.Writer , snapshotter , rawRef string , mode PullMode , insecure bool ) (* EnsuredImage , error ) {
4549 named , err := refdocker .ParseDockerRef (rawRef )
4650 if err != nil {
4751 return nil , err
@@ -70,11 +74,50 @@ func EnsureImage(ctx context.Context, client *containerd.Client, stdout io.Write
7074 return nil , errors .Errorf ("image %q is not available" , rawRef )
7175 }
7276
73- resolver , err := dockerconfigresolver .New (refdocker .Domain (named ))
77+ refDomain := refdocker .Domain (named )
78+
79+ var dOpts []dockerconfigresolver.Opt
80+ if insecure {
81+ logrus .Warnf ("skipping verifying HTTPS certs for %q" , refDomain )
82+ dOpts = append (dOpts , dockerconfigresolver .WithSkipVerifyCerts (true ))
83+ }
84+ resolver , err := dockerconfigresolver .New (refDomain , dOpts ... )
7485 if err != nil {
7586 return nil , err
7687 }
7788
89+ img , err := pullImage (ctx , client , stdout , snapshotter , resolver , ref )
90+ if err != nil {
91+ if ! IsErrHTTPResponseToHTTPSClient (err ) {
92+ return nil , err
93+ }
94+ if insecure {
95+ logrus .WithError (err ).Warnf ("server %q does not seem to support HTTPS, falling back to plain HTTP" , refDomain )
96+ dOpts = append (dOpts , dockerconfigresolver .WithPlainHTTP (true ))
97+ resolver , err = dockerconfigresolver .New (refDomain , dOpts ... )
98+ if err != nil {
99+ return nil , err
100+ }
101+ return pullImage (ctx , client , stdout , snapshotter , resolver , ref )
102+ } else {
103+ logrus .WithError (err ).Errorf ("server %q does not seem to support HTTPS" , refDomain )
104+ logrus .Info ("Hint: you may want to try --insecure-registry to allow plain HTTP (if you are in a trusted network)" )
105+ return nil , err
106+ }
107+ }
108+ return img , nil
109+ }
110+
111+ // IsErrHTTPResponseToHTTPSClient returns whether err is
112+ // "http: server gave HTTP response to HTTPS client"
113+ func IsErrHTTPResponseToHTTPSClient (err error ) bool {
114+ // The error string is unexposed as of Go 1.16, so we can't use `errors.Is`.
115+ // https://github.com/golang/go/issues/44855
116+ const unexposed = "server gave HTTP response to HTTPS client"
117+ return strings .Contains (err .Error (), unexposed )
118+ }
119+
120+ func pullImage (ctx context.Context , client * containerd.Client , stdout io.Writer , snapshotter string , resolver remotes.Resolver , ref string ) (* EnsuredImage , error ) {
78121 ctx , done , err := client .WithLease (ctx )
79122 if err != nil {
80123 return nil , err
@@ -109,6 +152,7 @@ func EnsureImage(ctx context.Context, client *containerd.Client, stdout io.Write
109152 Remote : sgz ,
110153 }
111154 return res , nil
155+
112156}
113157
114158func isStargz (sn string ) bool {
0 commit comments