Merge pull request #5602 from rhatdan/vendor
Update vendor of boltdb and containers/image
This commit is contained in:
		
						commit
						cc22b94a7a
					
				| 
						 | 
					@ -95,17 +95,19 @@ func loginCmd(c *cliconfig.LoginValues) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// username of user logged in to server (if one exists)
 | 
						// username of user logged in to server (if one exists)
 | 
				
			||||||
	userFromAuthFile, passFromAuthFile, err := config.GetAuthentication(sc, server)
 | 
						authConfig, err := config.GetCredentials(sc, server)
 | 
				
			||||||
	// Do not return error if no credentials found in credHelpers, new credentials will be stored by config.SetAuthentication
 | 
						// Do not return error if no credentials found in credHelpers, new credentials will be stored by config.SetAuthentication
 | 
				
			||||||
	if err != nil && err != credentials.NewErrCredentialsNotFound() {
 | 
						if err != nil && err != credentials.NewErrCredentialsNotFound() {
 | 
				
			||||||
		return errors.Wrapf(err, "error reading auth file")
 | 
							return errors.Wrapf(err, "error reading auth file")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if authConfig.IdentityToken != "" {
 | 
				
			||||||
 | 
							return errors.Errorf("currently logged in, auth file contains an Identity token")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if c.Flag("get-login").Changed {
 | 
						if c.Flag("get-login").Changed {
 | 
				
			||||||
		if userFromAuthFile == "" {
 | 
							if authConfig.Username == "" {
 | 
				
			||||||
			return errors.Errorf("not logged into %s", server)
 | 
								return errors.Errorf("not logged into %s", server)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Printf("%s\n", userFromAuthFile)
 | 
							fmt.Printf("%s\n", authConfig.Username)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,16 +131,16 @@ func loginCmd(c *cliconfig.LoginValues) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If no username and no password is specified, try to use existing ones.
 | 
						// If no username and no password is specified, try to use existing ones.
 | 
				
			||||||
	if c.Username == "" && password == "" && userFromAuthFile != "" && passFromAuthFile != "" {
 | 
						if c.Username == "" && password == "" && authConfig.Username == "" && authConfig.Password != "" {
 | 
				
			||||||
		fmt.Println("Authenticating with existing credentials...")
 | 
							fmt.Println("Authenticating with existing credentials...")
 | 
				
			||||||
		if err := docker.CheckAuth(ctx, sc, userFromAuthFile, passFromAuthFile, server); err == nil {
 | 
							if err := docker.CheckAuth(ctx, sc, authConfig.Username, authConfig.Password, server); err == nil {
 | 
				
			||||||
			fmt.Println("Existing credentials are valid. Already logged in to", server)
 | 
								fmt.Println("Existing credentials are valid. Already logged in to", server)
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Println("Existing credentials are invalid, please enter valid username and password")
 | 
							fmt.Println("Existing credentials are invalid, please enter valid username and password")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	username, password, err := getUserAndPass(c.Username, password, userFromAuthFile)
 | 
						username, password, err := getUserAndPass(c.Username, password, authConfig.Username)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return errors.Wrapf(err, "error getting username and password")
 | 
							return errors.Wrapf(err, "error getting username and password")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,12 +87,12 @@ func logoutCmd(c *cliconfig.LogoutValues) error {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	case config.ErrNotLoggedIn:
 | 
						case config.ErrNotLoggedIn:
 | 
				
			||||||
		// username of user logged in to server (if one exists)
 | 
							// username of user logged in to server (if one exists)
 | 
				
			||||||
		userFromAuthFile, passFromAuthFile, err := config.GetAuthentication(sc, server)
 | 
							authConfig, err := config.GetCredentials(sc, server)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return errors.Wrapf(err, "error reading auth file")
 | 
								return errors.Wrapf(err, "error reading auth file")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		islogin := docker.CheckAuth(getContext(), sc, userFromAuthFile, passFromAuthFile, server)
 | 
							islogin := docker.CheckAuth(getContext(), sc, authConfig.Username, authConfig.Password, server)
 | 
				
			||||||
		if userFromAuthFile != "" && passFromAuthFile != "" && islogin == nil {
 | 
							if authConfig.IdentityToken != "" && authConfig.Username != "" && authConfig.Password != "" && islogin == nil {
 | 
				
			||||||
			fmt.Printf("Not logged into %s with podman. Existing credentials were established via docker login. Please use docker logout instead.\n", server)
 | 
								fmt.Printf("Not logged into %s with podman. Existing credentials were established via docker login. Please use docker logout instead.\n", server)
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										4
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -12,7 +12,7 @@ require (
 | 
				
			||||||
	github.com/containers/buildah v1.14.5
 | 
						github.com/containers/buildah v1.14.5
 | 
				
			||||||
	github.com/containers/common v0.6.1
 | 
						github.com/containers/common v0.6.1
 | 
				
			||||||
	github.com/containers/conmon v2.0.10+incompatible
 | 
						github.com/containers/conmon v2.0.10+incompatible
 | 
				
			||||||
	github.com/containers/image/v5 v5.2.1
 | 
						github.com/containers/image/v5 v5.3.1
 | 
				
			||||||
	github.com/containers/psgo v1.4.0
 | 
						github.com/containers/psgo v1.4.0
 | 
				
			||||||
	github.com/containers/storage v1.16.6
 | 
						github.com/containers/storage v1.16.6
 | 
				
			||||||
	github.com/coreos/go-systemd/v22 v22.0.0
 | 
						github.com/coreos/go-systemd/v22 v22.0.0
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,6 @@ require (
 | 
				
			||||||
	github.com/docker/docker-credential-helpers v0.6.3
 | 
						github.com/docker/docker-credential-helpers v0.6.3
 | 
				
			||||||
	github.com/docker/go-connections v0.4.0
 | 
						github.com/docker/go-connections v0.4.0
 | 
				
			||||||
	github.com/docker/go-units v0.4.0
 | 
						github.com/docker/go-units v0.4.0
 | 
				
			||||||
	github.com/etcd-io/bbolt v1.3.3
 | 
					 | 
				
			||||||
	github.com/fsnotify/fsnotify v1.4.9
 | 
						github.com/fsnotify/fsnotify v1.4.9
 | 
				
			||||||
	github.com/ghodss/yaml v1.0.0
 | 
						github.com/ghodss/yaml v1.0.0
 | 
				
			||||||
	github.com/godbus/dbus/v5 v5.0.3
 | 
						github.com/godbus/dbus/v5 v5.0.3
 | 
				
			||||||
| 
						 | 
					@ -58,6 +57,7 @@ require (
 | 
				
			||||||
	github.com/uber/jaeger-lib v2.2.0+incompatible // indirect
 | 
						github.com/uber/jaeger-lib v2.2.0+incompatible // indirect
 | 
				
			||||||
	github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b
 | 
						github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b
 | 
				
			||||||
	github.com/vishvananda/netlink v1.1.0
 | 
						github.com/vishvananda/netlink v1.1.0
 | 
				
			||||||
 | 
						go.etcd.io/bbolt v1.3.4
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
 | 
						golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
 | 
				
			||||||
	golang.org/x/net v0.0.0-20200301022130-244492dfa37a
 | 
						golang.org/x/net v0.0.0-20200301022130-244492dfa37a
 | 
				
			||||||
	golang.org/x/sync v0.0.0-20190423024810-112230192c58
 | 
						golang.org/x/sync v0.0.0-20190423024810-112230192c58
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										15
									
								
								go.sum
								
								
								
								
							| 
						 | 
					@ -68,8 +68,9 @@ github.com/containers/common v0.6.1 h1:z9VeVXYeOnNV99uNLp7zoE5KO1n0hqz1mdm5a6AiI
 | 
				
			||||||
github.com/containers/common v0.6.1/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
 | 
					github.com/containers/common v0.6.1/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
 | 
				
			||||||
github.com/containers/conmon v2.0.10+incompatible h1:EiwL41r5vx8SxG+dyUmbJ3baV9GUWjijPOdCkzM6gWU=
 | 
					github.com/containers/conmon v2.0.10+incompatible h1:EiwL41r5vx8SxG+dyUmbJ3baV9GUWjijPOdCkzM6gWU=
 | 
				
			||||||
github.com/containers/conmon v2.0.10+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
 | 
					github.com/containers/conmon v2.0.10+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
 | 
				
			||||||
github.com/containers/image/v5 v5.2.1 h1:rQR6QSUneWBoW1bTFpP9EJJTevQFv27YsKYQVJIzg+s=
 | 
					 | 
				
			||||||
github.com/containers/image/v5 v5.2.1/go.mod h1:TfhmLwH+v1/HBVPIWH7diLs8XwcOkP3c7t7JFgqaUEc=
 | 
					github.com/containers/image/v5 v5.2.1/go.mod h1:TfhmLwH+v1/HBVPIWH7diLs8XwcOkP3c7t7JFgqaUEc=
 | 
				
			||||||
 | 
					github.com/containers/image/v5 v5.3.1 h1:AL0pR0d1ho3kLUAuBr+wnFlXuD3ChzKVljk0M8JBJHQ=
 | 
				
			||||||
 | 
					github.com/containers/image/v5 v5.3.1/go.mod h1:JnCfhbTIL9IxPPZm1JoQwiE0S9KET46M4OZySJsLylk=
 | 
				
			||||||
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
 | 
					github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
 | 
				
			||||||
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
 | 
					github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
 | 
				
			||||||
github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM=
 | 
					github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM=
 | 
				
			||||||
| 
						 | 
					@ -134,7 +135,6 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
 | 
				
			||||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 | 
					github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 | 
				
			||||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
					github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
				
			||||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
					github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
				
			||||||
github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM=
 | 
					 | 
				
			||||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 | 
					github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 | 
				
			||||||
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
					github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
				
			||||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
					github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
				
			||||||
| 
						 | 
					@ -433,8 +433,9 @@ github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMW
 | 
				
			||||||
github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
 | 
					github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
 | 
				
			||||||
github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 | 
					github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 | 
				
			||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
					github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
 | 
					 | 
				
			||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 | 
					github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 | 
				
			||||||
 | 
					github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4=
 | 
				
			||||||
 | 
					github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
				
			||||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 | 
					github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 | 
				
			||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 | 
					github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 | 
				
			||||||
github.com/urfave/cli/v2 v2.1.2-0.20200306124602-d648edd48d89/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
 | 
					github.com/urfave/cli/v2 v2.1.2-0.20200306124602-d648edd48d89/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
 | 
				
			||||||
| 
						 | 
					@ -442,8 +443,9 @@ github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b h1:hdDRrn9OP/roL8a/e/5Z
 | 
				
			||||||
github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b/go.mod h1:YHaw8N660ESgMgLOZfLQqT1htFItynAUxMesFBho52s=
 | 
					github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b/go.mod h1:YHaw8N660ESgMgLOZfLQqT1htFItynAUxMesFBho52s=
 | 
				
			||||||
github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE=
 | 
					github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE=
 | 
				
			||||||
github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
 | 
					github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
 | 
				
			||||||
github.com/vbauerster/mpb/v4 v4.11.2 h1:ynkUoKzi65DZ1UsQPx7sgi/KN6G9f7br+Us2nKm35AM=
 | 
					 | 
				
			||||||
github.com/vbauerster/mpb/v4 v4.11.2/go.mod h1:jIuIRCltGJUnm6DCyPVkwjlLUk4nHTH+m4eD14CdFF0=
 | 
					github.com/vbauerster/mpb/v4 v4.11.2/go.mod h1:jIuIRCltGJUnm6DCyPVkwjlLUk4nHTH+m4eD14CdFF0=
 | 
				
			||||||
 | 
					github.com/vbauerster/mpb/v4 v4.12.2 h1:TsBs1nWRYF0m8cUH13pxNhOUqY6yKcOr2PeSYxp2L3I=
 | 
				
			||||||
 | 
					github.com/vbauerster/mpb/v4 v4.12.2/go.mod h1:LVRGvMch8T4HQO3eg2pFPsACH9kO/O6fT/7vhGje3QE=
 | 
				
			||||||
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
 | 
					github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
 | 
				
			||||||
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
 | 
					github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
 | 
				
			||||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
 | 
					github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
 | 
				
			||||||
| 
						 | 
					@ -462,6 +464,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
 | 
				
			||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
					github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
				
			||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
					go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
				
			||||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
					go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
				
			||||||
 | 
					go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
 | 
				
			||||||
 | 
					go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
 | 
				
			||||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
 | 
					go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
 | 
				
			||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
					go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
				
			||||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
 | 
					go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
 | 
				
			||||||
| 
						 | 
					@ -475,6 +479,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
 | 
					golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
 | 
					golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
| 
						 | 
					@ -538,6 +543,8 @@ golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
 | 
					golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,10 @@ import (
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/containers/libpod/libpod/define"
 | 
						"github.com/containers/libpod/libpod/define"
 | 
				
			||||||
	bolt "github.com/etcd-io/bbolt"
 | 
					 | 
				
			||||||
	jsoniter "github.com/json-iterator/go"
 | 
						jsoniter "github.com/json-iterator/go"
 | 
				
			||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						bolt "go.etcd.io/bbolt"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
 | 
					var json = jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,9 +9,9 @@ import (
 | 
				
			||||||
	"github.com/containers/libpod/libpod/define"
 | 
						"github.com/containers/libpod/libpod/define"
 | 
				
			||||||
	"github.com/containers/libpod/pkg/rootless"
 | 
						"github.com/containers/libpod/pkg/rootless"
 | 
				
			||||||
	"github.com/containers/storage"
 | 
						"github.com/containers/storage"
 | 
				
			||||||
	bolt "github.com/etcd-io/bbolt"
 | 
					 | 
				
			||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						bolt "go.etcd.io/bbolt"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,13 +8,13 @@ import (
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"runtime"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/containers/image/v5/docker/reference"
 | 
						"github.com/containers/image/v5/docker/reference"
 | 
				
			||||||
	"github.com/containers/image/v5/image"
 | 
						"github.com/containers/image/v5/image"
 | 
				
			||||||
 | 
						"github.com/containers/image/v5/internal/pkg/platform"
 | 
				
			||||||
	"github.com/containers/image/v5/manifest"
 | 
						"github.com/containers/image/v5/manifest"
 | 
				
			||||||
	"github.com/containers/image/v5/pkg/blobinfocache"
 | 
						"github.com/containers/image/v5/pkg/blobinfocache"
 | 
				
			||||||
	"github.com/containers/image/v5/pkg/compression"
 | 
						"github.com/containers/image/v5/pkg/compression"
 | 
				
			||||||
| 
						 | 
					@ -356,11 +356,11 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, "", errors.Wrapf(err, "Error reading manifest list")
 | 
							return nil, "", errors.Wrapf(err, "Error reading manifest list")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	list, err := manifest.ListFromBlob(manifestList, manifestType)
 | 
						originalList, err := manifest.ListFromBlob(manifestList, manifestType)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, "", errors.Wrapf(err, "Error parsing manifest list %q", string(manifestList))
 | 
							return nil, "", errors.Wrapf(err, "Error parsing manifest list %q", string(manifestList))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	originalList := list.Clone()
 | 
						updatedList := originalList.Clone()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Read and/or clear the set of signatures for this list.
 | 
						// Read and/or clear the set of signatures for this list.
 | 
				
			||||||
	var sigs [][]byte
 | 
						var sigs [][]byte
 | 
				
			||||||
| 
						 | 
					@ -390,18 +390,18 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
 | 
				
			||||||
	case imgspecv1.MediaTypeImageManifest:
 | 
						case imgspecv1.MediaTypeImageManifest:
 | 
				
			||||||
		forceListMIMEType = imgspecv1.MediaTypeImageIndex
 | 
							forceListMIMEType = imgspecv1.MediaTypeImageIndex
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	selectedListType, err := c.determineListConversion(manifestType, c.dest.SupportedManifestMIMETypes(), forceListMIMEType)
 | 
						selectedListType, otherManifestMIMETypeCandidates, err := c.determineListConversion(manifestType, c.dest.SupportedManifestMIMETypes(), forceListMIMEType)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, "", errors.Wrapf(err, "Error determining manifest list type to write to destination")
 | 
							return nil, "", errors.Wrapf(err, "Error determining manifest list type to write to destination")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if selectedListType != list.MIMEType() {
 | 
						if selectedListType != originalList.MIMEType() {
 | 
				
			||||||
		if !canModifyManifestList {
 | 
							if !canModifyManifestList {
 | 
				
			||||||
			return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType)
 | 
								return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Copy each image, or just the ones we want to copy, in turn.
 | 
						// Copy each image, or just the ones we want to copy, in turn.
 | 
				
			||||||
	instanceDigests := list.Instances()
 | 
						instanceDigests := updatedList.Instances()
 | 
				
			||||||
	imagesToCopy := len(instanceDigests)
 | 
						imagesToCopy := len(instanceDigests)
 | 
				
			||||||
	if options.ImageListSelection == CopySpecificImages {
 | 
						if options.ImageListSelection == CopySpecificImages {
 | 
				
			||||||
		imagesToCopy = len(options.Instances)
 | 
							imagesToCopy = len(options.Instances)
 | 
				
			||||||
| 
						 | 
					@ -419,7 +419,7 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if skip {
 | 
								if skip {
 | 
				
			||||||
				update, err := list.Instance(instanceDigest)
 | 
									update, err := updatedList.Instance(instanceDigest)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, "", err
 | 
										return nil, "", err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -447,23 +447,31 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now reset the digest/size/types of the manifests in the list to account for any conversions that we made.
 | 
						// Now reset the digest/size/types of the manifests in the list to account for any conversions that we made.
 | 
				
			||||||
	if err = list.UpdateInstances(updates); err != nil {
 | 
						if err = updatedList.UpdateInstances(updates); err != nil {
 | 
				
			||||||
		return nil, "", errors.Wrapf(err, "Error updating manifest list")
 | 
							return nil, "", errors.Wrapf(err, "Error updating manifest list")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Perform the list conversion.
 | 
						// Iterate through supported list types, preferred format first.
 | 
				
			||||||
	if selectedListType != list.MIMEType() {
 | 
						c.Printf("Writing manifest list to image destination\n")
 | 
				
			||||||
		list, err = list.ConvertToMIMEType(selectedListType)
 | 
						var errs []string
 | 
				
			||||||
 | 
						for _, thisListType := range append([]string{selectedListType}, otherManifestMIMETypeCandidates...) {
 | 
				
			||||||
 | 
							attemptedList := updatedList
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							logrus.Debugf("Trying to use manifest list type %s…", thisListType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Perform the list conversion, if we need one.
 | 
				
			||||||
 | 
							if thisListType != updatedList.MIMEType() {
 | 
				
			||||||
 | 
								attemptedList, err = updatedList.ConvertToMIMEType(thisListType)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
			return nil, "", errors.Wrapf(err, "Error converting manifest list to list with MIME type %q", selectedListType)
 | 
									return nil, "", errors.Wrapf(err, "Error converting manifest list to list with MIME type %q", thisListType)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Check if the updates or a type conversion meaningfully changed the list of images
 | 
							// Check if the updates or a type conversion meaningfully changed the list of images
 | 
				
			||||||
		// by serializing them both so that we can compare them.
 | 
							// by serializing them both so that we can compare them.
 | 
				
			||||||
	updatedManifestList, err := list.Serialize()
 | 
							attemptedManifestList, err := attemptedList.Serialize()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
		return nil, "", errors.Wrapf(err, "Error encoding updated manifest list (%q: %#v)", list.MIMEType(), list.Instances())
 | 
								return nil, "", errors.Wrapf(err, "Error encoding updated manifest list (%q: %#v)", updatedList.MIMEType(), updatedList.Instances())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		originalManifestList, err := originalList.Serialize()
 | 
							originalManifestList, err := originalList.Serialize()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -471,18 +479,26 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// If we can't just use the original value, but we have to change it, flag an error.
 | 
							// If we can't just use the original value, but we have to change it, flag an error.
 | 
				
			||||||
	if !bytes.Equal(updatedManifestList, originalManifestList) {
 | 
							if !bytes.Equal(attemptedManifestList, originalManifestList) {
 | 
				
			||||||
			if !canModifyManifestList {
 | 
								if !canModifyManifestList {
 | 
				
			||||||
			return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType)
 | 
									return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", thisListType)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		manifestList = updatedManifestList
 | 
					 | 
				
			||||||
			logrus.Debugf("Manifest list has been updated")
 | 
								logrus.Debugf("Manifest list has been updated")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Save the manifest list.
 | 
							// Save the manifest list.
 | 
				
			||||||
	c.Printf("Writing manifest list to image destination\n")
 | 
							err = c.dest.PutManifest(ctx, attemptedManifestList, nil)
 | 
				
			||||||
	if err = c.dest.PutManifest(ctx, manifestList, nil); err != nil {
 | 
							if err != nil {
 | 
				
			||||||
		return nil, "", errors.Wrapf(err, "Error writing manifest list %q", string(manifestList))
 | 
								logrus.Debugf("Upload of manifest list type %s failed: %v", thisListType, err)
 | 
				
			||||||
 | 
								errs = append(errs, fmt.Sprintf("%s(%v)", thisListType, err))
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							errs = nil
 | 
				
			||||||
 | 
							manifestList = attemptedManifestList
 | 
				
			||||||
 | 
							break
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if errs != nil {
 | 
				
			||||||
 | 
							return nil, "", fmt.Errorf("Uploading manifest list failed, attempted the following formats: %s", strings.Join(errs, ", "))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Sign the manifest list.
 | 
						// Sign the manifest list.
 | 
				
			||||||
| 
						 | 
					@ -527,15 +543,6 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
 | 
				
			||||||
		return nil, "", "", errors.Wrapf(err, "Error initializing image from source %s", transports.ImageName(c.rawSource.Reference()))
 | 
							return nil, "", "", errors.Wrapf(err, "Error initializing image from source %s", transports.ImageName(c.rawSource.Reference()))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: Remove src.SupportsEncryption call and interface once copyUpdatedConfigAndManifest does not depend on source Image manifest type
 | 
					 | 
				
			||||||
	// Currently, the way copyUpdatedConfigAndManifest updates the manifest is to apply updates to the source manifest and call PutManifest
 | 
					 | 
				
			||||||
	// of the modified source manifest. The implication is that schemas like docker2 cannot be encrypted even though the destination
 | 
					 | 
				
			||||||
	// supports encryption because docker2 struct does not have annotations, which are required.
 | 
					 | 
				
			||||||
	// Reference to issue: https://github.com/containers/image/issues/746
 | 
					 | 
				
			||||||
	if options.OciEncryptLayers != nil && !src.SupportsEncryption(ctx) {
 | 
					 | 
				
			||||||
		return nil, "", "", errors.Errorf("Encryption request but not supported by source transport %s", src.Reference().Transport().Name())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// If the destination is a digested reference, make a note of that, determine what digest value we're
 | 
						// If the destination is a digested reference, make a note of that, determine what digest value we're
 | 
				
			||||||
	// expecting, and check that the source manifest matches it.  If the source manifest doesn't, but it's
 | 
						// expecting, and check that the source manifest matches it.  If the source manifest doesn't, but it's
 | 
				
			||||||
	// one item from a manifest list that matches it, accept that as a match.
 | 
						// one item from a manifest list that matches it, accept that as a match.
 | 
				
			||||||
| 
						 | 
					@ -708,21 +715,26 @@ func checkImageDestinationForCurrentRuntime(ctx context.Context, sys *types.Syst
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return errors.Wrapf(err, "Error parsing image configuration")
 | 
								return errors.Wrapf(err, "Error parsing image configuration")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							wantedPlatforms, err := platform.WantedPlatforms(sys)
 | 
				
			||||||
		wantedOS := runtime.GOOS
 | 
							if err != nil {
 | 
				
			||||||
		if sys != nil && sys.OSChoice != "" {
 | 
								return errors.Wrapf(err, "error getting current platform information %#v", sys)
 | 
				
			||||||
			wantedOS = sys.OSChoice
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if wantedOS != c.OS {
 | 
					 | 
				
			||||||
			logrus.Infof("Image operating system mismatch: image uses %q, expecting %q", c.OS, wantedOS)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		wantedArch := runtime.GOARCH
 | 
							options := newOrderedSet()
 | 
				
			||||||
		if sys != nil && sys.ArchitectureChoice != "" {
 | 
							match := false
 | 
				
			||||||
			wantedArch = sys.ArchitectureChoice
 | 
							for _, wantedPlatform := range wantedPlatforms {
 | 
				
			||||||
 | 
								// Waiting for https://github.com/opencontainers/image-spec/pull/777 :
 | 
				
			||||||
 | 
								// This currently can’t use image.MatchesPlatform because we don’t know what to use
 | 
				
			||||||
 | 
								// for image.Variant.
 | 
				
			||||||
 | 
								if wantedPlatform.OS == c.OS && wantedPlatform.Architecture == c.Architecture {
 | 
				
			||||||
 | 
									match = true
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		if wantedArch != c.Architecture {
 | 
								options.append(fmt.Sprintf("%s+%s", wantedPlatform.OS, wantedPlatform.Architecture))
 | 
				
			||||||
			logrus.Infof("Image architecture mismatch: image uses %q, expecting %q", c.Architecture, wantedArch)
 | 
							}
 | 
				
			||||||
 | 
							if !match {
 | 
				
			||||||
 | 
								logrus.Infof("Image operating system mismatch: image uses OS %q+architecture %q, expecting one of %q",
 | 
				
			||||||
 | 
									c.OS, c.Architecture, strings.Join(options.list, ", "))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
| 
						 | 
					@ -833,21 +845,24 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	func() { // A scope for defer
 | 
						if err := func() error { // A scope for defer
 | 
				
			||||||
		progressPool, progressCleanup := ic.c.newProgressPool(ctx)
 | 
							progressPool, progressCleanup := ic.c.newProgressPool(ctx)
 | 
				
			||||||
		defer progressCleanup()
 | 
							defer progressCleanup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for i, srcLayer := range srcInfos {
 | 
							for i, srcLayer := range srcInfos {
 | 
				
			||||||
			err = copySemaphore.Acquire(ctx, 1)
 | 
								err = copySemaphore.Acquire(ctx, 1)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				logrus.Debug("Can't acquire semaphoer", err)
 | 
									return errors.Wrapf(err, "Can't acquire semaphore")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			go copyLayerHelper(i, srcLayer, encLayerBitmap[i], progressPool)
 | 
								go copyLayerHelper(i, srcLayer, encLayerBitmap[i], progressPool)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Wait for all layers to be copied
 | 
							// Wait for all layers to be copied
 | 
				
			||||||
		copyGroup.Wait()
 | 
							copyGroup.Wait()
 | 
				
			||||||
	}()
 | 
							return nil
 | 
				
			||||||
 | 
						}(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	destInfos := make([]types.BlobInfo, numLayers)
 | 
						destInfos := make([]types.BlobInfo, numLayers)
 | 
				
			||||||
	diffIDs := make([]digest.Digest, numLayers)
 | 
						diffIDs := make([]digest.Digest, numLayers)
 | 
				
			||||||
| 
						 | 
					@ -1006,7 +1021,7 @@ func (c *copier) copyConfig(ctx context.Context, src types.Image) error {
 | 
				
			||||||
			return destInfo, nil
 | 
								return destInfo, nil
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if destInfo.Digest != srcInfo.Digest {
 | 
							if destInfo.Digest != srcInfo.Digest {
 | 
				
			||||||
			return errors.Errorf("Internal error: copying uncompressed config blob %s changed digest to %s", srcInfo.Digest, destInfo.Digest)
 | 
								return errors.Errorf("Internal error: copying uncompressed config blob %s changed digest to %s", srcInfo.Digest, destInfo.Digest)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import (
 | 
				
			||||||
// Include v2s1 signed but not v2s1 unsigned, because docker/distribution requires a signature even if the unsigned MIME type is used.
 | 
					// Include v2s1 signed but not v2s1 unsigned, because docker/distribution requires a signature even if the unsigned MIME type is used.
 | 
				
			||||||
var preferredManifestMIMETypes = []string{manifest.DockerV2Schema2MediaType, manifest.DockerV2Schema1SignedMediaType}
 | 
					var preferredManifestMIMETypes = []string{manifest.DockerV2Schema2MediaType, manifest.DockerV2Schema1SignedMediaType}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// orderedSet is a list of strings (MIME types in our case), with each string appearing at most once.
 | 
					// orderedSet is a list of strings (MIME types or platform descriptors in our case), with each string appearing at most once.
 | 
				
			||||||
type orderedSet struct {
 | 
					type orderedSet struct {
 | 
				
			||||||
	list     []string
 | 
						list     []string
 | 
				
			||||||
	included map[string]struct{}
 | 
						included map[string]struct{}
 | 
				
			||||||
| 
						 | 
					@ -125,8 +125,10 @@ func isMultiImage(ctx context.Context, img types.UnparsedImage) (bool, error) {
 | 
				
			||||||
// determineListConversion takes the current MIME type of a list of manifests,
 | 
					// determineListConversion takes the current MIME type of a list of manifests,
 | 
				
			||||||
// the list of MIME types supported for a given destination, and a possible
 | 
					// the list of MIME types supported for a given destination, and a possible
 | 
				
			||||||
// forced value, and returns the MIME type to which we should convert the list
 | 
					// forced value, and returns the MIME type to which we should convert the list
 | 
				
			||||||
// of manifests, whether we are converting to it or using it unmodified.
 | 
					// of manifests (regardless of whether we are converting to it or using it
 | 
				
			||||||
func (c *copier) determineListConversion(currentListMIMEType string, destSupportedMIMETypes []string, forcedListMIMEType string) (string, error) {
 | 
					// unmodified) and a slice of other list types which might be supported by the
 | 
				
			||||||
 | 
					// destination.
 | 
				
			||||||
 | 
					func (c *copier) determineListConversion(currentListMIMEType string, destSupportedMIMETypes []string, forcedListMIMEType string) (string, []string, error) {
 | 
				
			||||||
	// If there's no list of supported types, then anything we support is expected to be supported.
 | 
						// If there's no list of supported types, then anything we support is expected to be supported.
 | 
				
			||||||
	if len(destSupportedMIMETypes) == 0 {
 | 
						if len(destSupportedMIMETypes) == 0 {
 | 
				
			||||||
		destSupportedMIMETypes = manifest.SupportedListMIMETypes
 | 
							destSupportedMIMETypes = manifest.SupportedListMIMETypes
 | 
				
			||||||
| 
						 | 
					@ -136,6 +138,7 @@ func (c *copier) determineListConversion(currentListMIMEType string, destSupport
 | 
				
			||||||
		destSupportedMIMETypes = []string{forcedListMIMEType}
 | 
							destSupportedMIMETypes = []string{forcedListMIMEType}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var selectedType string
 | 
						var selectedType string
 | 
				
			||||||
 | 
						var otherSupportedTypes []string
 | 
				
			||||||
	for i := range destSupportedMIMETypes {
 | 
						for i := range destSupportedMIMETypes {
 | 
				
			||||||
		// The second priority is the first member of the list of acceptable types that is a list,
 | 
							// The second priority is the first member of the list of acceptable types that is a list,
 | 
				
			||||||
		// but keep going in case current type occurs later in the list.
 | 
							// but keep going in case current type occurs later in the list.
 | 
				
			||||||
| 
						 | 
					@ -148,15 +151,21 @@ func (c *copier) determineListConversion(currentListMIMEType string, destSupport
 | 
				
			||||||
			selectedType = destSupportedMIMETypes[i]
 | 
								selectedType = destSupportedMIMETypes[i]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Pick out the other list types that we support.
 | 
				
			||||||
 | 
						for i := range destSupportedMIMETypes {
 | 
				
			||||||
 | 
							if selectedType != destSupportedMIMETypes[i] && manifest.MIMETypeIsMultiImage(destSupportedMIMETypes[i]) {
 | 
				
			||||||
 | 
								otherSupportedTypes = append(otherSupportedTypes, destSupportedMIMETypes[i])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	logrus.Debugf("Manifest list has MIME type %s, ordered candidate list [%s]", currentListMIMEType, strings.Join(destSupportedMIMETypes, ", "))
 | 
						logrus.Debugf("Manifest list has MIME type %s, ordered candidate list [%s]", currentListMIMEType, strings.Join(destSupportedMIMETypes, ", "))
 | 
				
			||||||
	if selectedType == "" {
 | 
						if selectedType == "" {
 | 
				
			||||||
		return "", errors.Errorf("destination does not support any supported manifest list types (%v)", manifest.SupportedListMIMETypes)
 | 
							return "", nil, errors.Errorf("destination does not support any supported manifest list types (%v)", manifest.SupportedListMIMETypes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if selectedType != currentListMIMEType {
 | 
						if selectedType != currentListMIMEType {
 | 
				
			||||||
		logrus.Debugf("... will convert to %s", selectedType)
 | 
							logrus.Debugf("... will convert to %s first, and then try %v", selectedType, otherSupportedTypes)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		logrus.Debugf("... will use the original manifest list type")
 | 
							logrus.Debugf("... will use the original manifest list type, and then try %v", otherSupportedTypes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Done.
 | 
						// Done.
 | 
				
			||||||
	return selectedType, nil
 | 
						return selectedType, otherSupportedTypes, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,10 +41,10 @@ func (t archiveTransport) ValidatePolicyConfigurationScope(scope string) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// archiveReference is an ImageReference for Docker images.
 | 
					// archiveReference is an ImageReference for Docker images.
 | 
				
			||||||
type archiveReference struct {
 | 
					type archiveReference struct {
 | 
				
			||||||
	// only used for destinations
 | 
						path string
 | 
				
			||||||
 | 
						// only used for destinations,
 | 
				
			||||||
	// archiveReference.destinationRef is optional and can be nil for destinations as well.
 | 
						// archiveReference.destinationRef is optional and can be nil for destinations as well.
 | 
				
			||||||
	destinationRef reference.NamedTagged
 | 
						destinationRef reference.NamedTagged
 | 
				
			||||||
	path           string
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an Docker ImageReference.
 | 
					// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an Docker ImageReference.
 | 
				
			||||||
| 
						 | 
					@ -64,11 +64,6 @@ func ParseReference(refString string) (types.ImageReference, error) {
 | 
				
			||||||
			return nil, errors.Wrapf(err, "docker-archive parsing reference")
 | 
								return nil, errors.Wrapf(err, "docker-archive parsing reference")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ref = reference.TagNameOnly(ref)
 | 
							ref = reference.TagNameOnly(ref)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if _, isDigest := ref.(reference.Canonical); isDigest {
 | 
					 | 
				
			||||||
			return nil, errors.Errorf("docker-archive doesn't support digest references: %s", refString)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		refTagged, isTagged := ref.(reference.NamedTagged)
 | 
							refTagged, isTagged := ref.(reference.NamedTagged)
 | 
				
			||||||
		if !isTagged {
 | 
							if !isTagged {
 | 
				
			||||||
			// Really shouldn't be hit...
 | 
								// Really shouldn't be hit...
 | 
				
			||||||
| 
						 | 
					@ -77,9 +72,20 @@ func ParseReference(refString string) (types.ImageReference, error) {
 | 
				
			||||||
		destinationRef = refTagged
 | 
							destinationRef = refTagged
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NewReference(path, destinationRef)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewReference rethrns a Docker archive reference for a path and an optional destination reference.
 | 
				
			||||||
 | 
					func NewReference(path string, destinationRef reference.NamedTagged) (types.ImageReference, error) {
 | 
				
			||||||
 | 
						if strings.Contains(path, ":") {
 | 
				
			||||||
 | 
							return nil, errors.Errorf("Invalid docker-archive: reference: colon in path %q is not supported", path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, isDigest := destinationRef.(reference.Canonical); isDigest {
 | 
				
			||||||
 | 
							return nil, errors.Errorf("docker-archive doesn't support digest references: %s", destinationRef.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return archiveReference{
 | 
						return archiveReference{
 | 
				
			||||||
		destinationRef: destinationRef,
 | 
					 | 
				
			||||||
		path:           path,
 | 
							path:           path,
 | 
				
			||||||
 | 
							destinationRef: destinationRef,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,13 @@
 | 
				
			||||||
package docker
 | 
					package docker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
| 
						 | 
					@ -21,6 +23,7 @@ import (
 | 
				
			||||||
	"github.com/containers/image/v5/pkg/sysregistriesv2"
 | 
						"github.com/containers/image/v5/pkg/sysregistriesv2"
 | 
				
			||||||
	"github.com/containers/image/v5/pkg/tlsclientconfig"
 | 
						"github.com/containers/image/v5/pkg/tlsclientconfig"
 | 
				
			||||||
	"github.com/containers/image/v5/types"
 | 
						"github.com/containers/image/v5/types"
 | 
				
			||||||
 | 
						"github.com/containers/storage/pkg/homedir"
 | 
				
			||||||
	clientLib "github.com/docker/distribution/registry/client"
 | 
						clientLib "github.com/docker/distribution/registry/client"
 | 
				
			||||||
	"github.com/docker/go-connections/tlsconfig"
 | 
						"github.com/docker/go-connections/tlsconfig"
 | 
				
			||||||
	digest "github.com/opencontainers/go-digest"
 | 
						digest "github.com/opencontainers/go-digest"
 | 
				
			||||||
| 
						 | 
					@ -51,7 +54,18 @@ const (
 | 
				
			||||||
	backoffMaxDelay      = 60 * time.Second
 | 
						backoffMaxDelay      = 60 * time.Second
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var systemPerHostCertDirPaths = [2]string{"/etc/containers/certs.d", "/etc/docker/certs.d"}
 | 
					type certPath struct {
 | 
				
			||||||
 | 
						path     string
 | 
				
			||||||
 | 
						absolute bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						homeCertDir     = filepath.FromSlash(".config/containers/certs.d")
 | 
				
			||||||
 | 
						perHostCertDirs = []certPath{
 | 
				
			||||||
 | 
							{path: "/etc/containers/certs.d", absolute: true},
 | 
				
			||||||
 | 
							{path: "/etc/docker/certs.d", absolute: true},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go:
 | 
					// extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go:
 | 
				
			||||||
// signature represents a Docker image signature.
 | 
					// signature represents a Docker image signature.
 | 
				
			||||||
| 
						 | 
					@ -85,8 +99,8 @@ type dockerClient struct {
 | 
				
			||||||
	// by detectProperties(). Callers can edit tlsClientConfig.InsecureSkipVerify in the meantime.
 | 
						// by detectProperties(). Callers can edit tlsClientConfig.InsecureSkipVerify in the meantime.
 | 
				
			||||||
	tlsClientConfig *tls.Config
 | 
						tlsClientConfig *tls.Config
 | 
				
			||||||
	// The following members are not set by newDockerClient and must be set by callers if needed.
 | 
						// The following members are not set by newDockerClient and must be set by callers if needed.
 | 
				
			||||||
	username      string
 | 
						auth          types.DockerAuthConfig
 | 
				
			||||||
	password      string
 | 
						registryToken string
 | 
				
			||||||
	signatureBase signatureStorageBase
 | 
						signatureBase signatureStorageBase
 | 
				
			||||||
	scope         authScope
 | 
						scope         authScope
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -166,11 +180,12 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) {
 | 
				
			||||||
		hostCertDir     string
 | 
							hostCertDir     string
 | 
				
			||||||
		fullCertDirPath string
 | 
							fullCertDirPath string
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	for _, systemPerHostCertDirPath := range systemPerHostCertDirPaths {
 | 
					
 | 
				
			||||||
		if sys != nil && sys.RootForImplicitAbsolutePaths != "" {
 | 
						for _, perHostCertDir := range append([]certPath{{path: filepath.Join(homedir.Get(), homeCertDir), absolute: false}}, perHostCertDirs...) {
 | 
				
			||||||
			hostCertDir = filepath.Join(sys.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
 | 
							if sys != nil && sys.RootForImplicitAbsolutePaths != "" && perHostCertDir.absolute {
 | 
				
			||||||
 | 
								hostCertDir = filepath.Join(sys.RootForImplicitAbsolutePaths, perHostCertDir.path)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			hostCertDir = systemPerHostCertDirPath
 | 
								hostCertDir = perHostCertDir.path
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fullCertDirPath = filepath.Join(hostCertDir, hostPort)
 | 
							fullCertDirPath = filepath.Join(hostCertDir, hostPort)
 | 
				
			||||||
| 
						 | 
					@ -196,10 +211,11 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) {
 | 
				
			||||||
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
 | 
					// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
 | 
				
			||||||
func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) {
 | 
					func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) {
 | 
				
			||||||
	registry := reference.Domain(ref.ref)
 | 
						registry := reference.Domain(ref.ref)
 | 
				
			||||||
	username, password, err := config.GetAuthentication(sys, registry)
 | 
						auth, err := config.GetCredentials(sys, registry)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrapf(err, "error getting username and password")
 | 
							return nil, errors.Wrapf(err, "error getting username and password")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sigBase, err := configuredSignatureStorageBase(sys, ref, write)
 | 
						sigBase, err := configuredSignatureStorageBase(sys, ref, write)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
| 
						 | 
					@ -209,8 +225,10 @@ func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	client.username = username
 | 
						client.auth = auth
 | 
				
			||||||
	client.password = password
 | 
						if sys != nil {
 | 
				
			||||||
 | 
							client.registryToken = sys.DockerBearerRegistryToken
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	client.signatureBase = sigBase
 | 
						client.signatureBase = sigBase
 | 
				
			||||||
	client.scope.actions = actions
 | 
						client.scope.actions = actions
 | 
				
			||||||
	client.scope.remoteName = reference.Path(ref.ref)
 | 
						client.scope.remoteName = reference.Path(ref.ref)
 | 
				
			||||||
| 
						 | 
					@ -252,7 +270,7 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if reg != nil {
 | 
						if reg != nil {
 | 
				
			||||||
		if reg.Blocked {
 | 
							if reg.Blocked {
 | 
				
			||||||
			return nil, fmt.Errorf("registry %s is blocked in %s", reg.Prefix, sysregistriesv2.ConfigPath(sys))
 | 
								return nil, fmt.Errorf("registry %s is blocked in %s or %s", reg.Prefix, sysregistriesv2.ConfigPath(sys), sysregistriesv2.ConfigDirPath(sys))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		skipVerify = reg.Insecure
 | 
							skipVerify = reg.Insecure
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -272,8 +290,10 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return errors.Wrapf(err, "error creating new docker client")
 | 
							return errors.Wrapf(err, "error creating new docker client")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	client.username = username
 | 
						client.auth = types.DockerAuthConfig{
 | 
				
			||||||
	client.password = password
 | 
							Username: username,
 | 
				
			||||||
 | 
							Password: password,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth, nil)
 | 
						resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -315,7 +335,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
 | 
				
			||||||
	v1Res := &V1Results{}
 | 
						v1Res := &V1Results{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get credentials from authfile for the underlying hostname
 | 
						// Get credentials from authfile for the underlying hostname
 | 
				
			||||||
	username, password, err := config.GetAuthentication(sys, registry)
 | 
						auth, err := config.GetCredentials(sys, registry)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrapf(err, "error getting username and password")
 | 
							return nil, errors.Wrapf(err, "error getting username and password")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -333,8 +353,10 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrapf(err, "error creating new docker client")
 | 
							return nil, errors.Wrapf(err, "error creating new docker client")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	client.username = username
 | 
						client.auth = auth
 | 
				
			||||||
	client.password = password
 | 
						if sys != nil {
 | 
				
			||||||
 | 
							client.registryToken = sys.DockerBearerRegistryToken
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Only try the v1 search endpoint if the search query is not empty. If it is
 | 
						// Only try the v1 search endpoint if the search query is not empty. If it is
 | 
				
			||||||
	// empty skip to the v2 endpoint.
 | 
						// empty skip to the v2 endpoint.
 | 
				
			||||||
| 
						 | 
					@ -515,9 +537,11 @@ func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope
 | 
				
			||||||
		schemeNames = append(schemeNames, challenge.Scheme)
 | 
							schemeNames = append(schemeNames, challenge.Scheme)
 | 
				
			||||||
		switch challenge.Scheme {
 | 
							switch challenge.Scheme {
 | 
				
			||||||
		case "basic":
 | 
							case "basic":
 | 
				
			||||||
			req.SetBasicAuth(c.username, c.password)
 | 
								req.SetBasicAuth(c.auth.Username, c.auth.Password)
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		case "bearer":
 | 
							case "bearer":
 | 
				
			||||||
 | 
								registryToken := c.registryToken
 | 
				
			||||||
 | 
								if registryToken == "" {
 | 
				
			||||||
				cacheKey := ""
 | 
									cacheKey := ""
 | 
				
			||||||
				scopes := []authScope{c.scope}
 | 
									scopes := []authScope{c.scope}
 | 
				
			||||||
				if extraScope != nil {
 | 
									if extraScope != nil {
 | 
				
			||||||
| 
						 | 
					@ -531,14 +555,25 @@ func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope
 | 
				
			||||||
					token = t.(bearerToken)
 | 
										token = t.(bearerToken)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if !inCache || time.Now().After(token.expirationTime) {
 | 
									if !inCache || time.Now().After(token.expirationTime) {
 | 
				
			||||||
				t, err := c.getBearerToken(req.Context(), challenge, scopes)
 | 
										var (
 | 
				
			||||||
 | 
											t   *bearerToken
 | 
				
			||||||
 | 
											err error
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
										if c.auth.IdentityToken != "" {
 | 
				
			||||||
 | 
											t, err = c.getBearerTokenOAuth2(req.Context(), challenge, scopes)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											t, err = c.getBearerToken(req.Context(), challenge, scopes)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						return err
 | 
											return err
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					token = *t
 | 
										token = *t
 | 
				
			||||||
					c.tokenCache.Store(cacheKey, token)
 | 
										c.tokenCache.Store(cacheKey, token)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.Token))
 | 
									registryToken = token.Token
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", registryToken))
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			logrus.Debugf("no handler for %s authentication", challenge.Scheme)
 | 
								logrus.Debugf("no handler for %s authentication", challenge.Scheme)
 | 
				
			||||||
| 
						 | 
					@ -548,48 +583,96 @@ func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge, scopes []authScope) (*bearerToken, error) {
 | 
					func (c *dockerClient) getBearerTokenOAuth2(ctx context.Context, challenge challenge,
 | 
				
			||||||
 | 
						scopes []authScope) (*bearerToken, error) {
 | 
				
			||||||
	realm, ok := challenge.Parameters["realm"]
 | 
						realm, ok := challenge.Parameters["realm"]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return nil, errors.Errorf("missing realm in bearer auth challenge")
 | 
							return nil, errors.Errorf("missing realm in bearer auth challenge")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authReq, err := http.NewRequest("GET", realm, nil)
 | 
						authReq, err := http.NewRequest(http.MethodPost, realm, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authReq = authReq.WithContext(ctx)
 | 
						authReq = authReq.WithContext(ctx)
 | 
				
			||||||
	getParams := authReq.URL.Query()
 | 
					
 | 
				
			||||||
	if c.username != "" {
 | 
						// Make the form data required against the oauth2 authentication
 | 
				
			||||||
		getParams.Add("account", c.username)
 | 
						// More details here: https://docs.docker.com/registry/spec/auth/oauth/
 | 
				
			||||||
	}
 | 
						params := authReq.URL.Query()
 | 
				
			||||||
	if service, ok := challenge.Parameters["service"]; ok && service != "" {
 | 
						if service, ok := challenge.Parameters["service"]; ok && service != "" {
 | 
				
			||||||
		getParams.Add("service", service)
 | 
							params.Add("service", service)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, scope := range scopes {
 | 
						for _, scope := range scopes {
 | 
				
			||||||
		if scope.remoteName != "" && scope.actions != "" {
 | 
							if scope.remoteName != "" && scope.actions != "" {
 | 
				
			||||||
			getParams.Add("scope", fmt.Sprintf("repository:%s:%s", scope.remoteName, scope.actions))
 | 
								params.Add("scope", fmt.Sprintf("repository:%s:%s", scope.remoteName, scope.actions))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	authReq.URL.RawQuery = getParams.Encode()
 | 
						params.Add("grant_type", "refresh_token")
 | 
				
			||||||
	if c.username != "" && c.password != "" {
 | 
						params.Add("refresh_token", c.auth.IdentityToken)
 | 
				
			||||||
		authReq.SetBasicAuth(c.username, c.password)
 | 
					
 | 
				
			||||||
	}
 | 
						authReq.Body = ioutil.NopCloser(bytes.NewBufferString(params.Encode()))
 | 
				
			||||||
 | 
						authReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
 | 
				
			||||||
	logrus.Debugf("%s %s", authReq.Method, authReq.URL.String())
 | 
						logrus.Debugf("%s %s", authReq.Method, authReq.URL.String())
 | 
				
			||||||
	res, err := c.client.Do(authReq)
 | 
						res, err := c.client.Do(authReq)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer res.Body.Close()
 | 
						defer res.Body.Close()
 | 
				
			||||||
	switch res.StatusCode {
 | 
						if err := httpResponseToError(res, "Trying to obtain access token"); err != nil {
 | 
				
			||||||
	case http.StatusUnauthorized:
 | 
							return nil, err
 | 
				
			||||||
		err := clientLib.HandleErrorResponse(res)
 | 
						}
 | 
				
			||||||
		logrus.Debugf("Server response when trying to obtain an access token: \n%q", err.Error())
 | 
					
 | 
				
			||||||
		return nil, ErrUnauthorizedForCredentials{Err: err}
 | 
						tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
 | 
				
			||||||
	case http.StatusOK:
 | 
						if err != nil {
 | 
				
			||||||
		break
 | 
							return nil, err
 | 
				
			||||||
	default:
 | 
						}
 | 
				
			||||||
		return nil, errors.Errorf("unexpected http code: %d (%s), URL: %s", res.StatusCode, http.StatusText(res.StatusCode), authReq.URL)
 | 
					
 | 
				
			||||||
 | 
						return newBearerTokenFromJSONBlob(tokenBlob)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
 | 
				
			||||||
 | 
						scopes []authScope) (*bearerToken, error) {
 | 
				
			||||||
 | 
						realm, ok := challenge.Parameters["realm"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, errors.Errorf("missing realm in bearer auth challenge")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						authReq, err := http.NewRequest(http.MethodGet, realm, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						authReq = authReq.WithContext(ctx)
 | 
				
			||||||
 | 
						params := authReq.URL.Query()
 | 
				
			||||||
 | 
						if c.auth.Username != "" {
 | 
				
			||||||
 | 
							params.Add("account", c.auth.Username)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if service, ok := challenge.Parameters["service"]; ok && service != "" {
 | 
				
			||||||
 | 
							params.Add("service", service)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, scope := range scopes {
 | 
				
			||||||
 | 
							if scope.remoteName != "" && scope.actions != "" {
 | 
				
			||||||
 | 
								params.Add("scope", fmt.Sprintf("repository:%s:%s", scope.remoteName, scope.actions))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						authReq.URL.RawQuery = params.Encode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c.auth.Username != "" && c.auth.Password != "" {
 | 
				
			||||||
 | 
							authReq.SetBasicAuth(c.auth.Username, c.auth.Password)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logrus.Debugf("%s %s", authReq.Method, authReq.URL.String())
 | 
				
			||||||
 | 
						res, err := c.client.Do(authReq)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer res.Body.Close()
 | 
				
			||||||
 | 
						if err := httpResponseToError(res, "Requesting bear token"); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
 | 
						tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,6 +108,7 @@ func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, pullSo
 | 
				
			||||||
	if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(ref.ref) != primaryDomain {
 | 
						if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(ref.ref) != primaryDomain {
 | 
				
			||||||
		copy := *endpointSys
 | 
							copy := *endpointSys
 | 
				
			||||||
		copy.DockerAuthConfig = nil
 | 
							copy.DockerAuthConfig = nil
 | 
				
			||||||
 | 
							copy.DockerBearerRegistryToken = ""
 | 
				
			||||||
		endpointSys = ©
 | 
							endpointSys = ©
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -249,6 +249,9 @@ func (s *Source) ensureCachedDataIsPresentPrivate() error {
 | 
				
			||||||
	if err := json.Unmarshal(configBytes, &parsedConfig); err != nil {
 | 
						if err := json.Unmarshal(configBytes, &parsedConfig); err != nil {
 | 
				
			||||||
		return errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config)
 | 
							return errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if parsedConfig.RootFS == nil {
 | 
				
			||||||
 | 
							return errors.Errorf("Invalid image config (rootFS is not set): %s", tarManifest[0].Config)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig)
 | 
						knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,7 +56,7 @@ func (m *manifestSchema1) ConfigBlob(context.Context) ([]byte, error) {
 | 
				
			||||||
// layers in the resulting configuration isn't guaranteed to be returned to due how
 | 
					// layers in the resulting configuration isn't guaranteed to be returned to due how
 | 
				
			||||||
// old image manifests work (docker v2s1 especially).
 | 
					// old image manifests work (docker v2s1 especially).
 | 
				
			||||||
func (m *manifestSchema1) OCIConfig(ctx context.Context) (*imgspecv1.Image, error) {
 | 
					func (m *manifestSchema1) OCIConfig(ctx context.Context) (*imgspecv1.Image, error) {
 | 
				
			||||||
	v2s2, err := m.convertToManifestSchema2(nil, nil)
 | 
						v2s2, err := m.convertToManifestSchema2(ctx, &types.ManifestUpdateOptions{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -107,6 +107,24 @@ func (m *manifestSchema1) UpdatedImageNeedsLayerDiffIDs(options types.ManifestUp
 | 
				
			||||||
// This does not change the state of the original Image object.
 | 
					// This does not change the state of the original Image object.
 | 
				
			||||||
func (m *manifestSchema1) UpdatedImage(ctx context.Context, options types.ManifestUpdateOptions) (types.Image, error) {
 | 
					func (m *manifestSchema1) UpdatedImage(ctx context.Context, options types.ManifestUpdateOptions) (types.Image, error) {
 | 
				
			||||||
	copy := manifestSchema1{m: manifest.Schema1Clone(m.m)}
 | 
						copy := manifestSchema1{m: manifest.Schema1Clone(m.m)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We have 2 MIME types for schema 1, which are basically equivalent (even the un-"Signed" MIME type will be rejected if there isn’t a signature; so,
 | 
				
			||||||
 | 
						// handle conversions between them by doing nothing.
 | 
				
			||||||
 | 
						if options.ManifestMIMEType != manifest.DockerV2Schema1MediaType && options.ManifestMIMEType != manifest.DockerV2Schema1SignedMediaType {
 | 
				
			||||||
 | 
							converted, err := convertManifestIfRequiredWithUpdate(ctx, options, map[string]manifestConvertFn{
 | 
				
			||||||
 | 
								imgspecv1.MediaTypeImageManifest:  copy.convertToManifestOCI1,
 | 
				
			||||||
 | 
								manifest.DockerV2Schema2MediaType: copy.convertToManifestSchema2Generic,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if converted != nil {
 | 
				
			||||||
 | 
								return converted, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// No conversion required, update manifest
 | 
				
			||||||
	if options.LayerInfos != nil {
 | 
						if options.LayerInfos != nil {
 | 
				
			||||||
		if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
 | 
							if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
| 
						 | 
					@ -121,36 +139,30 @@ func (m *manifestSchema1) UpdatedImage(ctx context.Context, options types.Manife
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch options.ManifestMIMEType {
 | 
					 | 
				
			||||||
	case "": // No conversion, OK
 | 
					 | 
				
			||||||
	case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
 | 
					 | 
				
			||||||
	// We have 2 MIME types for schema 1, which are basically equivalent (even the un-"Signed" MIME type will be rejected if there isn’t a signature; so,
 | 
					 | 
				
			||||||
	// handle conversions between them by doing nothing.
 | 
					 | 
				
			||||||
	case manifest.DockerV2Schema2MediaType:
 | 
					 | 
				
			||||||
		m2, err := copy.convertToManifestSchema2(options.InformationOnly.LayerInfos, options.InformationOnly.LayerDiffIDs)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return memoryImageFromManifest(m2), nil
 | 
					 | 
				
			||||||
	case imgspecv1.MediaTypeImageManifest:
 | 
					 | 
				
			||||||
		// We can't directly convert to OCI, but we can transitively convert via a Docker V2.2 Distribution manifest
 | 
					 | 
				
			||||||
		m2, err := copy.convertToManifestSchema2(options.InformationOnly.LayerInfos, options.InformationOnly.LayerDiffIDs)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return m2.UpdatedImage(ctx, types.ManifestUpdateOptions{
 | 
					 | 
				
			||||||
			ManifestMIMEType: imgspecv1.MediaTypeImageManifest,
 | 
					 | 
				
			||||||
			InformationOnly:  options.InformationOnly,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return nil, errors.Errorf("Conversion of image manifest from %s to %s is not implemented", manifest.DockerV2Schema1SignedMediaType, options.ManifestMIMEType)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return memoryImageFromManifest(©), nil
 | 
						return memoryImageFromManifest(©), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// convertToManifestSchema2Generic returns a genericManifest implementation converted to manifest.DockerV2Schema2MediaType.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original manifestSchema1 object.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// We need this function just because a function returning an implementation of the genericManifest
 | 
				
			||||||
 | 
					// interface is not automatically assignable to a function type returning the genericManifest interface
 | 
				
			||||||
 | 
					func (m *manifestSchema1) convertToManifestSchema2Generic(ctx context.Context, options *types.ManifestUpdateOptions) (genericManifest, error) {
 | 
				
			||||||
 | 
						return m.convertToManifestSchema2(ctx, options)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// convertToManifestSchema2 returns a genericManifest implementation converted to manifest.DockerV2Schema2MediaType.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original manifestSchema1 object.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
// Based on github.com/docker/docker/distribution/pull_v2.go
 | 
					// Based on github.com/docker/docker/distribution/pull_v2.go
 | 
				
			||||||
func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.BlobInfo, layerDiffIDs []digest.Digest) (genericManifest, error) {
 | 
					func (m *manifestSchema1) convertToManifestSchema2(_ context.Context, options *types.ManifestUpdateOptions) (*manifestSchema2, error) {
 | 
				
			||||||
 | 
						uploadedLayerInfos := options.InformationOnly.LayerInfos
 | 
				
			||||||
 | 
						layerDiffIDs := options.InformationOnly.LayerDiffIDs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(m.m.ExtractedV1Compatibility) == 0 {
 | 
						if len(m.m.ExtractedV1Compatibility) == 0 {
 | 
				
			||||||
		// What would this even mean?! Anyhow, the rest of the code depends on FSLayers[0] and ExtractedV1Compatibility[0] existing.
 | 
							// What would this even mean?! Anyhow, the rest of the code depends on FSLayers[0] and ExtractedV1Compatibility[0] existing.
 | 
				
			||||||
		return nil, errors.Errorf("Cannot convert an image with 0 history entries to %s", manifest.DockerV2Schema2MediaType)
 | 
							return nil, errors.Errorf("Cannot convert an image with 0 history entries to %s", manifest.DockerV2Schema2MediaType)
 | 
				
			||||||
| 
						 | 
					@ -165,6 +177,15 @@ func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.Bl
 | 
				
			||||||
		return nil, errors.Errorf("Internal error: collected %d DiffID values, but schema1 manifest has %d fsLayers", len(layerDiffIDs), len(m.m.FSLayers))
 | 
							return nil, errors.Errorf("Internal error: collected %d DiffID values, but schema1 manifest has %d fsLayers", len(layerDiffIDs), len(m.m.FSLayers))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var convertedLayerUpdates []types.BlobInfo // Only used if options.LayerInfos != nil
 | 
				
			||||||
 | 
						if options.LayerInfos != nil {
 | 
				
			||||||
 | 
							if len(options.LayerInfos) != len(m.m.FSLayers) {
 | 
				
			||||||
 | 
								return nil, errors.Errorf("Error converting image: layer edits for %d layers vs %d existing layers",
 | 
				
			||||||
 | 
									len(options.LayerInfos), len(m.m.FSLayers))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							convertedLayerUpdates = []types.BlobInfo{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Build a list of the diffIDs for the non-empty layers.
 | 
						// Build a list of the diffIDs for the non-empty layers.
 | 
				
			||||||
	diffIDs := []digest.Digest{}
 | 
						diffIDs := []digest.Digest{}
 | 
				
			||||||
	var layers []manifest.Schema2Descriptor
 | 
						var layers []manifest.Schema2Descriptor
 | 
				
			||||||
| 
						 | 
					@ -185,6 +206,9 @@ func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.Bl
 | 
				
			||||||
				Size:      size,
 | 
									Size:      size,
 | 
				
			||||||
				Digest:    m.m.FSLayers[v1Index].BlobSum,
 | 
									Digest:    m.m.FSLayers[v1Index].BlobSum,
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
								if options.LayerInfos != nil {
 | 
				
			||||||
 | 
									convertedLayerUpdates = append(convertedLayerUpdates, options.LayerInfos[v2Index])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			diffIDs = append(diffIDs, d)
 | 
								diffIDs = append(diffIDs, d)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -198,9 +222,26 @@ func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.Bl
 | 
				
			||||||
		Digest:    digest.FromBytes(configJSON),
 | 
							Digest:    digest.FromBytes(configJSON),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if options.LayerInfos != nil {
 | 
				
			||||||
 | 
							options.LayerInfos = convertedLayerUpdates
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return manifestSchema2FromComponents(configDescriptor, nil, configJSON, layers), nil
 | 
						return manifestSchema2FromComponents(configDescriptor, nil, configJSON, layers), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// convertToManifestOCI1 returns a genericManifest implementation converted to imgspecv1.MediaTypeImageManifest.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original manifestSchema1 object.
 | 
				
			||||||
 | 
					func (m *manifestSchema1) convertToManifestOCI1(ctx context.Context, options *types.ManifestUpdateOptions) (genericManifest, error) {
 | 
				
			||||||
 | 
						// We can't directly convert to OCI, but we can transitively convert via a Docker V2.2 Distribution manifest
 | 
				
			||||||
 | 
						m2, err := m.convertToManifestSchema2(ctx, options)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return m2.convertToManifestOCI1(ctx, options)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SupportsEncryption returns if encryption is supported for the manifest type
 | 
					// SupportsEncryption returns if encryption is supported for the manifest type
 | 
				
			||||||
func (m *manifestSchema1) SupportsEncryption(context.Context) bool {
 | 
					func (m *manifestSchema1) SupportsEncryption(context.Context) bool {
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,7 +50,7 @@ func manifestSchema2FromManifest(src types.ImageSource, manifestBlob []byte) (ge
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// manifestSchema2FromComponents builds a new manifestSchema2 from the supplied data:
 | 
					// manifestSchema2FromComponents builds a new manifestSchema2 from the supplied data:
 | 
				
			||||||
func manifestSchema2FromComponents(config manifest.Schema2Descriptor, src types.ImageSource, configBlob []byte, layers []manifest.Schema2Descriptor) genericManifest {
 | 
					func manifestSchema2FromComponents(config manifest.Schema2Descriptor, src types.ImageSource, configBlob []byte, layers []manifest.Schema2Descriptor) *manifestSchema2 {
 | 
				
			||||||
	return &manifestSchema2{
 | 
						return &manifestSchema2{
 | 
				
			||||||
		src:        src,
 | 
							src:        src,
 | 
				
			||||||
		configBlob: configBlob,
 | 
							configBlob: configBlob,
 | 
				
			||||||
| 
						 | 
					@ -160,6 +160,21 @@ func (m *manifestSchema2) UpdatedImage(ctx context.Context, options types.Manife
 | 
				
			||||||
		configBlob: m.configBlob,
 | 
							configBlob: m.configBlob,
 | 
				
			||||||
		m:          manifest.Schema2Clone(m.m),
 | 
							m:          manifest.Schema2Clone(m.m),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						converted, err := convertManifestIfRequiredWithUpdate(ctx, options, map[string]manifestConvertFn{
 | 
				
			||||||
 | 
							manifest.DockerV2Schema1MediaType:       copy.convertToManifestSchema1,
 | 
				
			||||||
 | 
							manifest.DockerV2Schema1SignedMediaType: copy.convertToManifestSchema1,
 | 
				
			||||||
 | 
							imgspecv1.MediaTypeImageManifest:        copy.convertToManifestOCI1,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if converted != nil {
 | 
				
			||||||
 | 
							return converted, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// No conversion required, update manifest
 | 
				
			||||||
	if options.LayerInfos != nil {
 | 
						if options.LayerInfos != nil {
 | 
				
			||||||
		if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
 | 
							if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
| 
						 | 
					@ -167,16 +182,6 @@ func (m *manifestSchema2) UpdatedImage(ctx context.Context, options types.Manife
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1 to schema2, but we really don't care.
 | 
						// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1 to schema2, but we really don't care.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch options.ManifestMIMEType {
 | 
					 | 
				
			||||||
	case "": // No conversion, OK
 | 
					 | 
				
			||||||
	case manifest.DockerV2Schema1SignedMediaType, manifest.DockerV2Schema1MediaType:
 | 
					 | 
				
			||||||
		return copy.convertToManifestSchema1(ctx, options.InformationOnly.Destination)
 | 
					 | 
				
			||||||
	case imgspecv1.MediaTypeImageManifest:
 | 
					 | 
				
			||||||
		return copy.convertToManifestOCI1(ctx)
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return nil, errors.Errorf("Conversion of image manifest from %s to %s is not implemented", manifest.DockerV2Schema2MediaType, options.ManifestMIMEType)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return memoryImageFromManifest(©), nil
 | 
						return memoryImageFromManifest(©), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -189,7 +194,11 @@ func oci1DescriptorFromSchema2Descriptor(d manifest.Schema2Descriptor) imgspecv1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *manifestSchema2) convertToManifestOCI1(ctx context.Context) (types.Image, error) {
 | 
					// convertToManifestOCI1 returns a genericManifest implementation converted to imgspecv1.MediaTypeImageManifest.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original manifestSchema2 object.
 | 
				
			||||||
 | 
					func (m *manifestSchema2) convertToManifestOCI1(ctx context.Context, _ *types.ManifestUpdateOptions) (genericManifest, error) {
 | 
				
			||||||
	configOCI, err := m.OCIConfig(ctx)
 | 
						configOCI, err := m.OCIConfig(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
| 
						 | 
					@ -222,12 +231,27 @@ func (m *manifestSchema2) convertToManifestOCI1(ctx context.Context) (types.Imag
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m1 := manifestOCI1FromComponents(config, m.src, configOCIBytes, layers)
 | 
						return manifestOCI1FromComponents(config, m.src, configOCIBytes, layers), nil
 | 
				
			||||||
	return memoryImageFromManifest(m1), nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// convertToManifestSchema1 returns a genericManifest implementation converted to manifest.DockerV2Schema1{Signed,}MediaType.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original manifestSchema2 object.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
// Based on docker/distribution/manifest/schema1/config_builder.go
 | 
					// Based on docker/distribution/manifest/schema1/config_builder.go
 | 
				
			||||||
func (m *manifestSchema2) convertToManifestSchema1(ctx context.Context, dest types.ImageDestination) (types.Image, error) {
 | 
					func (m *manifestSchema2) convertToManifestSchema1(ctx context.Context, options *types.ManifestUpdateOptions) (genericManifest, error) {
 | 
				
			||||||
 | 
						dest := options.InformationOnly.Destination
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var convertedLayerUpdates []types.BlobInfo // Only used if options.LayerInfos != nil
 | 
				
			||||||
 | 
						if options.LayerInfos != nil {
 | 
				
			||||||
 | 
							if len(options.LayerInfos) != len(m.m.LayersDescriptors) {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("Error converting image: layer edits for %d layers vs %d existing layers",
 | 
				
			||||||
 | 
									len(options.LayerInfos), len(m.m.LayersDescriptors))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							convertedLayerUpdates = []types.BlobInfo{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	configBytes, err := m.ConfigBlob(ctx)
 | 
						configBytes, err := m.ConfigBlob(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
| 
						 | 
					@ -254,24 +278,32 @@ func (m *manifestSchema2) convertToManifestSchema1(ctx context.Context, dest typ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var blobDigest digest.Digest
 | 
							var blobDigest digest.Digest
 | 
				
			||||||
		if historyEntry.EmptyLayer {
 | 
							if historyEntry.EmptyLayer {
 | 
				
			||||||
 | 
								emptyLayerBlobInfo := types.BlobInfo{Digest: GzippedEmptyLayerDigest, Size: int64(len(GzippedEmptyLayer))}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !haveGzippedEmptyLayer {
 | 
								if !haveGzippedEmptyLayer {
 | 
				
			||||||
				logrus.Debugf("Uploading empty layer during conversion to schema 1")
 | 
									logrus.Debugf("Uploading empty layer during conversion to schema 1")
 | 
				
			||||||
				// Ideally we should update the relevant BlobInfoCache about this layer, but that would require passing it down here,
 | 
									// Ideally we should update the relevant BlobInfoCache about this layer, but that would require passing it down here,
 | 
				
			||||||
				// and anyway this blob is so small that it’s easier to just copy it than to worry about figuring out another location where to get it.
 | 
									// and anyway this blob is so small that it’s easier to just copy it than to worry about figuring out another location where to get it.
 | 
				
			||||||
				info, err := dest.PutBlob(ctx, bytes.NewReader(GzippedEmptyLayer), types.BlobInfo{Digest: GzippedEmptyLayerDigest, Size: int64(len(GzippedEmptyLayer))}, none.NoCache, false)
 | 
									info, err := dest.PutBlob(ctx, bytes.NewReader(GzippedEmptyLayer), emptyLayerBlobInfo, none.NoCache, false)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, errors.Wrap(err, "Error uploading empty layer")
 | 
										return nil, errors.Wrap(err, "Error uploading empty layer")
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if info.Digest != GzippedEmptyLayerDigest {
 | 
									if info.Digest != emptyLayerBlobInfo.Digest {
 | 
				
			||||||
					return nil, errors.Errorf("Internal error: Uploaded empty layer has digest %#v instead of %s", info.Digest, GzippedEmptyLayerDigest)
 | 
										return nil, errors.Errorf("Internal error: Uploaded empty layer has digest %#v instead of %s", info.Digest, emptyLayerBlobInfo.Digest)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				haveGzippedEmptyLayer = true
 | 
									haveGzippedEmptyLayer = true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			blobDigest = GzippedEmptyLayerDigest
 | 
								if options.LayerInfos != nil {
 | 
				
			||||||
 | 
									convertedLayerUpdates = append(convertedLayerUpdates, emptyLayerBlobInfo)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								blobDigest = emptyLayerBlobInfo.Digest
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			if nonemptyLayerIndex >= len(m.m.LayersDescriptors) {
 | 
								if nonemptyLayerIndex >= len(m.m.LayersDescriptors) {
 | 
				
			||||||
				return nil, errors.Errorf("Invalid image configuration, needs more than the %d distributed layers", len(m.m.LayersDescriptors))
 | 
									return nil, errors.Errorf("Invalid image configuration, needs more than the %d distributed layers", len(m.m.LayersDescriptors))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if options.LayerInfos != nil {
 | 
				
			||||||
 | 
									convertedLayerUpdates = append(convertedLayerUpdates, options.LayerInfos[nonemptyLayerIndex])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			blobDigest = m.m.LayersDescriptors[nonemptyLayerIndex].Digest
 | 
								blobDigest = m.m.LayersDescriptors[nonemptyLayerIndex].Digest
 | 
				
			||||||
			nonemptyLayerIndex++
 | 
								nonemptyLayerIndex++
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -313,11 +345,14 @@ func (m *manifestSchema2) convertToManifestSchema1(ctx context.Context, dest typ
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	history[0].V1Compatibility = string(v1Config)
 | 
						history[0].V1Compatibility = string(v1Config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if options.LayerInfos != nil {
 | 
				
			||||||
 | 
							options.LayerInfos = convertedLayerUpdates
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	m1, err := manifestSchema1FromComponents(dest.Reference().DockerReference(), fsLayers, history, imageConfig.Architecture)
 | 
						m1, err := manifestSchema1FromComponents(dest.Reference().DockerReference(), fsLayers, history, imageConfig.Architecture)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err // This should never happen, we should have created all the components correctly.
 | 
							return nil, err // This should never happen, we should have created all the components correctly.
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return memoryImageFromManifest(m1), nil
 | 
						return m1, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func v1IDFromBlobDigestAndComponents(blobDigest digest.Digest, others ...string) (string, error) {
 | 
					func v1IDFromBlobDigestAndComponents(blobDigest digest.Digest, others ...string) (string, error) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import (
 | 
				
			||||||
	"github.com/containers/image/v5/manifest"
 | 
						"github.com/containers/image/v5/manifest"
 | 
				
			||||||
	"github.com/containers/image/v5/types"
 | 
						"github.com/containers/image/v5/types"
 | 
				
			||||||
	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
 | 
						imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// genericManifest is an interface for parsing, modifying image manifests and related data.
 | 
					// genericManifest is an interface for parsing, modifying image manifests and related data.
 | 
				
			||||||
| 
						 | 
					@ -45,6 +46,10 @@ type genericManifest interface {
 | 
				
			||||||
	// This does not change the state of the original Image object.
 | 
						// This does not change the state of the original Image object.
 | 
				
			||||||
	UpdatedImage(ctx context.Context, options types.ManifestUpdateOptions) (types.Image, error)
 | 
						UpdatedImage(ctx context.Context, options types.ManifestUpdateOptions) (types.Image, error)
 | 
				
			||||||
	// SupportsEncryption returns if encryption is supported for the manifest type
 | 
						// SupportsEncryption returns if encryption is supported for the manifest type
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Deprecated: Initially used to determine if a manifest can be copied from a source manifest type since
 | 
				
			||||||
 | 
						// the process of updating a manifest between different manifest types was to update then convert.
 | 
				
			||||||
 | 
						// This resulted in some fields in the update being lost. This has been fixed by: https://github.com/containers/image/pull/836
 | 
				
			||||||
	SupportsEncryption(ctx context.Context) bool
 | 
						SupportsEncryption(ctx context.Context) bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,3 +80,34 @@ func manifestLayerInfosToBlobInfos(layers []manifest.LayerInfo) []types.BlobInfo
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return blobs
 | 
						return blobs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// manifestConvertFn (a method of genericManifest object) returns a genericManifest implementation
 | 
				
			||||||
 | 
					// converted to a specific manifest MIME type.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original genericManifest object.
 | 
				
			||||||
 | 
					type manifestConvertFn func(ctx context.Context, options *types.ManifestUpdateOptions) (genericManifest, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// convertManifestIfRequiredWithUpdate will run conversion functions of a manifest if
 | 
				
			||||||
 | 
					// required and re-apply the options to the converted type.
 | 
				
			||||||
 | 
					// It returns (nil, nil) if no conversion was requested.
 | 
				
			||||||
 | 
					func convertManifestIfRequiredWithUpdate(ctx context.Context, options types.ManifestUpdateOptions, converters map[string]manifestConvertFn) (types.Image, error) {
 | 
				
			||||||
 | 
						if options.ManifestMIMEType == "" {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						converter, ok := converters[options.ManifestMIMEType]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, errors.Errorf("Unsupported conversion type: %v", options.ManifestMIMEType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						optionsCopy := options
 | 
				
			||||||
 | 
						convertedManifest, err := converter(ctx, &optionsCopy)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						convertedImage := memoryImageFromManifest(convertedManifest)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						optionsCopy.ManifestMIMEType = ""
 | 
				
			||||||
 | 
						return convertedImage.UpdatedImage(ctx, optionsCopy)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -140,6 +140,21 @@ func (m *manifestOCI1) UpdatedImage(ctx context.Context, options types.ManifestU
 | 
				
			||||||
		configBlob: m.configBlob,
 | 
							configBlob: m.configBlob,
 | 
				
			||||||
		m:          manifest.OCI1Clone(m.m),
 | 
							m:          manifest.OCI1Clone(m.m),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						converted, err := convertManifestIfRequiredWithUpdate(ctx, options, map[string]manifestConvertFn{
 | 
				
			||||||
 | 
							manifest.DockerV2Schema2MediaType:       copy.convertToManifestSchema2Generic,
 | 
				
			||||||
 | 
							manifest.DockerV2Schema1MediaType:       copy.convertToManifestSchema1,
 | 
				
			||||||
 | 
							manifest.DockerV2Schema1SignedMediaType: copy.convertToManifestSchema1,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if converted != nil {
 | 
				
			||||||
 | 
							return converted, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// No conversion required, update manifest
 | 
				
			||||||
	if options.LayerInfos != nil {
 | 
						if options.LayerInfos != nil {
 | 
				
			||||||
		if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
 | 
							if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
| 
						 | 
					@ -147,24 +162,6 @@ func (m *manifestOCI1) UpdatedImage(ctx context.Context, options types.ManifestU
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care.
 | 
						// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch options.ManifestMIMEType {
 | 
					 | 
				
			||||||
	case "": // No conversion, OK
 | 
					 | 
				
			||||||
	case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
 | 
					 | 
				
			||||||
		// We can't directly convert to V1, but we can transitively convert via a V2 image
 | 
					 | 
				
			||||||
		m2, err := copy.convertToManifestSchema2()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return m2.UpdatedImage(ctx, types.ManifestUpdateOptions{
 | 
					 | 
				
			||||||
			ManifestMIMEType: options.ManifestMIMEType,
 | 
					 | 
				
			||||||
			InformationOnly:  options.InformationOnly,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	case manifest.DockerV2Schema2MediaType:
 | 
					 | 
				
			||||||
		return copy.convertToManifestSchema2()
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return nil, errors.Errorf("Conversion of image manifest from %s to %s is not implemented", imgspecv1.MediaTypeImageManifest, options.ManifestMIMEType)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return memoryImageFromManifest(©), nil
 | 
						return memoryImageFromManifest(©), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -177,7 +174,22 @@ func schema2DescriptorFromOCI1Descriptor(d imgspecv1.Descriptor) manifest.Schema
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *manifestOCI1) convertToManifestSchema2() (types.Image, error) {
 | 
					// convertToManifestSchema2Generic returns a genericManifest implementation converted to manifest.DockerV2Schema2MediaType.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original manifestSchema1 object.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// We need this function just because a function returning an implementation of the genericManifest
 | 
				
			||||||
 | 
					// interface is not automatically assignable to a function type returning the genericManifest interface
 | 
				
			||||||
 | 
					func (m *manifestOCI1) convertToManifestSchema2Generic(ctx context.Context, options *types.ManifestUpdateOptions) (genericManifest, error) {
 | 
				
			||||||
 | 
						return m.convertToManifestSchema2(ctx, options)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// convertToManifestSchema2 returns a genericManifest implementation converted to manifest.DockerV2Schema2MediaType.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original manifestOCI1 object.
 | 
				
			||||||
 | 
					func (m *manifestOCI1) convertToManifestSchema2(_ context.Context, _ *types.ManifestUpdateOptions) (*manifestSchema2, error) {
 | 
				
			||||||
	// Create a copy of the descriptor.
 | 
						// Create a copy of the descriptor.
 | 
				
			||||||
	config := schema2DescriptorFromOCI1Descriptor(m.m.Config)
 | 
						config := schema2DescriptorFromOCI1Descriptor(m.m.Config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -209,8 +221,21 @@ func (m *manifestOCI1) convertToManifestSchema2() (types.Image, error) {
 | 
				
			||||||
	// Rather than copying the ConfigBlob now, we just pass m.src to the
 | 
						// Rather than copying the ConfigBlob now, we just pass m.src to the
 | 
				
			||||||
	// translated manifest, since the only difference is the mediatype of
 | 
						// translated manifest, since the only difference is the mediatype of
 | 
				
			||||||
	// descriptors there is no change to any blob stored in m.src.
 | 
						// descriptors there is no change to any blob stored in m.src.
 | 
				
			||||||
	m1 := manifestSchema2FromComponents(config, m.src, nil, layers)
 | 
						return manifestSchema2FromComponents(config, m.src, nil, layers), nil
 | 
				
			||||||
	return memoryImageFromManifest(m1), nil
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// convertToManifestSchema1 returns a genericManifest implementation converted to manifest.DockerV2Schema1{Signed,}MediaType.
 | 
				
			||||||
 | 
					// It may use options.InformationOnly and also adjust *options to be appropriate for editing the returned
 | 
				
			||||||
 | 
					// value.
 | 
				
			||||||
 | 
					// This does not change the state of the original manifestOCI1 object.
 | 
				
			||||||
 | 
					func (m *manifestOCI1) convertToManifestSchema1(ctx context.Context, options *types.ManifestUpdateOptions) (genericManifest, error) {
 | 
				
			||||||
 | 
						// We can't directly convert to V1, but we can transitively convert via a V2 image
 | 
				
			||||||
 | 
						m2, err := m.convertToManifestSchema2(ctx, options)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return m2.convertToManifestSchema1(ctx, options)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SupportsEncryption returns if encryption is supported for the manifest type
 | 
					// SupportsEncryption returns if encryption is supported for the manifest type
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										203
									
								
								vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										203
									
								
								vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
					@ -0,0 +1,203 @@
 | 
				
			||||||
 | 
					package platform
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Largely based on
 | 
				
			||||||
 | 
					// https://github.com/moby/moby/blob/bc846d2e8fe5538220e0c31e9d0e8446f6fbc022/distribution/cpuinfo_unix.go
 | 
				
			||||||
 | 
					// Copyright 2012-2017 Docker, Inc.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// https://github.com/containerd/containerd/blob/726dcaea50883e51b2ec6db13caff0e7936b711d/platforms/cpuinfo.go
 | 
				
			||||||
 | 
					//    Copyright The containerd Authors.
 | 
				
			||||||
 | 
					//    Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					//    you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					//    You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//        http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//    Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					//    distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					//    See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					//    limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containers/image/v5/types"
 | 
				
			||||||
 | 
						imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For Linux, the kernel has already detected the ABI, ISA and Features.
 | 
				
			||||||
 | 
					// So we don't need to access the ARM registers to detect platform information
 | 
				
			||||||
 | 
					// by ourselves. We can just parse these information from /proc/cpuinfo
 | 
				
			||||||
 | 
					func getCPUInfo(pattern string) (info string, err error) {
 | 
				
			||||||
 | 
						if runtime.GOOS != "linux" {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("getCPUInfo for OS %s not implemented", runtime.GOOS)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cpuinfo, err := os.Open("/proc/cpuinfo")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer cpuinfo.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Start to Parse the Cpuinfo line by line. For SMP SoC, we parse
 | 
				
			||||||
 | 
						// the first core is enough.
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(cpuinfo)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							newline := scanner.Text()
 | 
				
			||||||
 | 
							list := strings.Split(newline, ":")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) {
 | 
				
			||||||
 | 
								return strings.TrimSpace(list[1]), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check whether the scanner encountered errors
 | 
				
			||||||
 | 
						err = scanner.Err()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return "", fmt.Errorf("getCPUInfo for pattern: %s not found", pattern)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getCPUVariantWindows() string {
 | 
				
			||||||
 | 
						// Windows only supports v7 for ARM32 and v8 for ARM64 and so we can use
 | 
				
			||||||
 | 
						// runtime.GOARCH to determine the variants
 | 
				
			||||||
 | 
						var variant string
 | 
				
			||||||
 | 
						switch runtime.GOARCH {
 | 
				
			||||||
 | 
						case "arm64":
 | 
				
			||||||
 | 
							variant = "v8"
 | 
				
			||||||
 | 
						case "arm":
 | 
				
			||||||
 | 
							variant = "v7"
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							variant = ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return variant
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getCPUVariantArm() string {
 | 
				
			||||||
 | 
						variant, err := getCPUInfo("Cpu architecture")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// TODO handle RPi Zero mismatch (https://github.com/moby/moby/pull/36121#issuecomment-398328286)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch strings.ToLower(variant) {
 | 
				
			||||||
 | 
						case "8", "aarch64":
 | 
				
			||||||
 | 
							variant = "v8"
 | 
				
			||||||
 | 
						case "7", "7m", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)":
 | 
				
			||||||
 | 
							variant = "v7"
 | 
				
			||||||
 | 
						case "6", "6tej":
 | 
				
			||||||
 | 
							variant = "v6"
 | 
				
			||||||
 | 
						case "5", "5t", "5te", "5tej":
 | 
				
			||||||
 | 
							variant = "v5"
 | 
				
			||||||
 | 
						case "4", "4t":
 | 
				
			||||||
 | 
							variant = "v4"
 | 
				
			||||||
 | 
						case "3":
 | 
				
			||||||
 | 
							variant = "v3"
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							variant = ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return variant
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getCPUVariant(os string, arch string) string {
 | 
				
			||||||
 | 
						if os == "windows" {
 | 
				
			||||||
 | 
							return getCPUVariantWindows()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if arch == "arm" || arch == "arm64" {
 | 
				
			||||||
 | 
							return getCPUVariantArm()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var compatibility = map[string][]string{
 | 
				
			||||||
 | 
						"arm":   {"v7", "v6", "v5"},
 | 
				
			||||||
 | 
						"arm64": {"v8"},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Returns all compatible platforms with the platform specifics possibly overriden by user,
 | 
				
			||||||
 | 
					// the most compatible platform is first.
 | 
				
			||||||
 | 
					// If some option (arch, os, variant) is not present, a value from current platform is detected.
 | 
				
			||||||
 | 
					func WantedPlatforms(ctx *types.SystemContext) ([]imgspecv1.Platform, error) {
 | 
				
			||||||
 | 
						wantedArch := runtime.GOARCH
 | 
				
			||||||
 | 
						wantedVariant := ""
 | 
				
			||||||
 | 
						if ctx != nil && ctx.ArchitectureChoice != "" {
 | 
				
			||||||
 | 
							wantedArch = ctx.ArchitectureChoice
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Only auto-detect the variant if we are using the default architecture.
 | 
				
			||||||
 | 
							// If the user has specified the ArchitectureChoice, don't autodetect, even if
 | 
				
			||||||
 | 
							// ctx.ArchitectureChoice == runtime.GOARCH, because we have no idea whether the runtime.GOARCH
 | 
				
			||||||
 | 
							// value is relevant to the use case, and if we do autodetect a variant,
 | 
				
			||||||
 | 
							// ctx.VariantChoice can't be used to override it back to "".
 | 
				
			||||||
 | 
							wantedVariant = getCPUVariant(runtime.GOOS, runtime.GOARCH)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ctx != nil && ctx.VariantChoice != "" {
 | 
				
			||||||
 | 
							wantedVariant = ctx.VariantChoice
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wantedOS := runtime.GOOS
 | 
				
			||||||
 | 
						if ctx != nil && ctx.OSChoice != "" {
 | 
				
			||||||
 | 
							wantedOS = ctx.OSChoice
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var wantedPlatforms []imgspecv1.Platform
 | 
				
			||||||
 | 
						if wantedVariant != "" && compatibility[wantedArch] != nil {
 | 
				
			||||||
 | 
							wantedPlatforms = make([]imgspecv1.Platform, 0, len(compatibility[wantedArch]))
 | 
				
			||||||
 | 
							wantedIndex := -1
 | 
				
			||||||
 | 
							for i, v := range compatibility[wantedArch] {
 | 
				
			||||||
 | 
								if wantedVariant == v {
 | 
				
			||||||
 | 
									wantedIndex = i
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// user wants a variant which we know nothing about - not even compatibility
 | 
				
			||||||
 | 
							if wantedIndex == -1 {
 | 
				
			||||||
 | 
								wantedPlatforms = []imgspecv1.Platform{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										OS:           wantedOS,
 | 
				
			||||||
 | 
										Architecture: wantedArch,
 | 
				
			||||||
 | 
										Variant:      wantedVariant,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								for i := wantedIndex; i < len(compatibility[wantedArch]); i++ {
 | 
				
			||||||
 | 
									v := compatibility[wantedArch][i]
 | 
				
			||||||
 | 
									wantedPlatforms = append(wantedPlatforms, imgspecv1.Platform{
 | 
				
			||||||
 | 
										OS:           wantedOS,
 | 
				
			||||||
 | 
										Architecture: wantedArch,
 | 
				
			||||||
 | 
										Variant:      v,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							wantedPlatforms = []imgspecv1.Platform{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									OS:           wantedOS,
 | 
				
			||||||
 | 
									Architecture: wantedArch,
 | 
				
			||||||
 | 
									Variant:      wantedVariant,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return wantedPlatforms, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func MatchesPlatform(image imgspecv1.Platform, wanted imgspecv1.Platform) bool {
 | 
				
			||||||
 | 
						if image.Architecture != wanted.Architecture {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if image.OS != wanted.OS {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if wanted.Variant == "" || image.Variant == wanted.Variant {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,8 @@ package manifest
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"runtime"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						platform "github.com/containers/image/v5/internal/pkg/platform"
 | 
				
			||||||
	"github.com/containers/image/v5/types"
 | 
						"github.com/containers/image/v5/types"
 | 
				
			||||||
	"github.com/opencontainers/go-digest"
 | 
						"github.com/opencontainers/go-digest"
 | 
				
			||||||
	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
 | 
						imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
 | 
				
			||||||
| 
						 | 
					@ -81,9 +81,6 @@ func (list *Schema2List) UpdateInstances(updates []ListUpdate) error {
 | 
				
			||||||
		if updates[i].MediaType == "" {
 | 
							if updates[i].MediaType == "" {
 | 
				
			||||||
			return errors.Errorf("update %d of %d passed to Schema2List.UpdateInstances had no media type (was %q)", i+1, len(updates), list.Manifests[i].MediaType)
 | 
								return errors.Errorf("update %d of %d passed to Schema2List.UpdateInstances had no media type (was %q)", i+1, len(updates), list.Manifests[i].MediaType)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err := SupportedSchema2MediaType(updates[i].MediaType); err != nil && SupportedOCI1MediaType(updates[i].MediaType) != nil {
 | 
					 | 
				
			||||||
			return errors.Wrapf(err, "update %d of %d passed to Schema2List.UpdateInstances had an unsupported media type (was %q): %q", i+1, len(updates), list.Manifests[i].MediaType, updates[i].MediaType)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		list.Manifests[i].MediaType = updates[i].MediaType
 | 
							list.Manifests[i].MediaType = updates[i].MediaType
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
| 
						 | 
					@ -92,21 +89,25 @@ func (list *Schema2List) UpdateInstances(updates []ListUpdate) error {
 | 
				
			||||||
// ChooseInstance parses blob as a schema2 manifest list, and returns the digest
 | 
					// ChooseInstance parses blob as a schema2 manifest list, and returns the digest
 | 
				
			||||||
// of the image which is appropriate for the current environment.
 | 
					// of the image which is appropriate for the current environment.
 | 
				
			||||||
func (list *Schema2List) ChooseInstance(ctx *types.SystemContext) (digest.Digest, error) {
 | 
					func (list *Schema2List) ChooseInstance(ctx *types.SystemContext) (digest.Digest, error) {
 | 
				
			||||||
	wantedArch := runtime.GOARCH
 | 
						wantedPlatforms, err := platform.WantedPlatforms(ctx)
 | 
				
			||||||
	if ctx != nil && ctx.ArchitectureChoice != "" {
 | 
						if err != nil {
 | 
				
			||||||
		wantedArch = ctx.ArchitectureChoice
 | 
							return "", errors.Wrapf(err, "error getting platform information %#v", ctx)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	wantedOS := runtime.GOOS
 | 
						for _, wantedPlatform := range wantedPlatforms {
 | 
				
			||||||
	if ctx != nil && ctx.OSChoice != "" {
 | 
					 | 
				
			||||||
		wantedOS = ctx.OSChoice
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for _, d := range list.Manifests {
 | 
							for _, d := range list.Manifests {
 | 
				
			||||||
		if d.Platform.Architecture == wantedArch && d.Platform.OS == wantedOS {
 | 
								imagePlatform := imgspecv1.Platform{
 | 
				
			||||||
 | 
									Architecture: d.Platform.Architecture,
 | 
				
			||||||
 | 
									OS:           d.Platform.OS,
 | 
				
			||||||
 | 
									OSVersion:    d.Platform.OSVersion,
 | 
				
			||||||
 | 
									OSFeatures:   dupStringSlice(d.Platform.OSFeatures),
 | 
				
			||||||
 | 
									Variant:      d.Platform.Variant,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if platform.MatchesPlatform(imagePlatform, wantedPlatform) {
 | 
				
			||||||
				return d.Digest, nil
 | 
									return d.Digest, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	return "", fmt.Errorf("no image found in manifest list for architecture %s, OS %s", wantedArch, wantedOS)
 | 
						}
 | 
				
			||||||
 | 
						return "", fmt.Errorf("no image found in manifest list for architecture %s, variant %s, OS %s", wantedPlatforms[0].Architecture, wantedPlatforms[0].Variant, wantedPlatforms[0].OS)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serialize returns the list in a blob format.
 | 
					// Serialize returns the list in a blob format.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,14 @@ type OCI1 struct {
 | 
				
			||||||
	imgspecv1.Manifest
 | 
						imgspecv1.Manifest
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SupportedOCI1MediaType checks if the specified string is a supported OCI1 media type.
 | 
					// SupportedOCI1MediaType checks if the specified string is a supported OCI1
 | 
				
			||||||
 | 
					// media type.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Deprecated: blindly rejecting unknown MIME types when the consumer does not
 | 
				
			||||||
 | 
					// need to process the input just reduces interoperability (and violates the
 | 
				
			||||||
 | 
					// standard) with no benefit, and that this function does not check that the
 | 
				
			||||||
 | 
					// media type is appropriate for any specific purpose, so it’s not all that
 | 
				
			||||||
 | 
					// useful for validation anyway.
 | 
				
			||||||
func SupportedOCI1MediaType(m string) error {
 | 
					func SupportedOCI1MediaType(m string) error {
 | 
				
			||||||
	switch m {
 | 
						switch m {
 | 
				
			||||||
	case imgspecv1.MediaTypeDescriptor, imgspecv1.MediaTypeImageConfig, imgspecv1.MediaTypeImageLayer, imgspecv1.MediaTypeImageLayerGzip, imgspecv1.MediaTypeImageLayerNonDistributable, imgspecv1.MediaTypeImageLayerNonDistributableGzip, imgspecv1.MediaTypeImageLayerNonDistributableZstd, imgspecv1.MediaTypeImageLayerZstd, imgspecv1.MediaTypeImageManifest, imgspecv1.MediaTypeLayoutHeader, ociencspec.MediaTypeLayerEnc, ociencspec.MediaTypeLayerGzipEnc:
 | 
						case imgspecv1.MediaTypeDescriptor, imgspecv1.MediaTypeImageConfig, imgspecv1.MediaTypeImageLayer, imgspecv1.MediaTypeImageLayerGzip, imgspecv1.MediaTypeImageLayerNonDistributable, imgspecv1.MediaTypeImageLayerNonDistributableGzip, imgspecv1.MediaTypeImageLayerNonDistributableZstd, imgspecv1.MediaTypeImageLayerZstd, imgspecv1.MediaTypeImageManifest, imgspecv1.MediaTypeLayoutHeader, ociencspec.MediaTypeLayerEnc, ociencspec.MediaTypeLayerGzipEnc:
 | 
				
			||||||
| 
						 | 
					@ -48,15 +55,6 @@ func OCI1FromManifest(manifest []byte) (*OCI1, error) {
 | 
				
			||||||
	if err := json.Unmarshal(manifest, &oci1); err != nil {
 | 
						if err := json.Unmarshal(manifest, &oci1); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Check manifest's and layers' media types.
 | 
					 | 
				
			||||||
	if err := SupportedOCI1MediaType(oci1.Config.MediaType); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, layer := range oci1.Layers {
 | 
					 | 
				
			||||||
		if err := SupportedOCI1MediaType(layer.MediaType); err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &oci1, nil
 | 
						return &oci1, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,11 +126,6 @@ func (m *OCI1) UpdateLayerInfos(layerInfos []types.BlobInfo) error {
 | 
				
			||||||
	m.Layers = make([]imgspecv1.Descriptor, len(layerInfos))
 | 
						m.Layers = make([]imgspecv1.Descriptor, len(layerInfos))
 | 
				
			||||||
	for i, info := range layerInfos {
 | 
						for i, info := range layerInfos {
 | 
				
			||||||
		mimeType := original[i].MediaType
 | 
							mimeType := original[i].MediaType
 | 
				
			||||||
		// First make sure we support the media type of the original layer.
 | 
					 | 
				
			||||||
		if err := SupportedOCI1MediaType(original[i].MediaType); err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("Error preparing updated manifest: unknown media type of original layer: %q", original[i].MediaType)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if info.CryptoOperation == types.Decrypt {
 | 
							if info.CryptoOperation == types.Decrypt {
 | 
				
			||||||
			decMimeType, err := getDecryptedMediaType(mimeType)
 | 
								decMimeType, err := getDecryptedMediaType(mimeType)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						platform "github.com/containers/image/v5/internal/pkg/platform"
 | 
				
			||||||
	"github.com/containers/image/v5/types"
 | 
						"github.com/containers/image/v5/types"
 | 
				
			||||||
	"github.com/opencontainers/go-digest"
 | 
						"github.com/opencontainers/go-digest"
 | 
				
			||||||
	imgspec "github.com/opencontainers/image-spec/specs-go"
 | 
						imgspec "github.com/opencontainers/image-spec/specs-go"
 | 
				
			||||||
| 
						 | 
					@ -64,9 +65,6 @@ func (index *OCI1Index) UpdateInstances(updates []ListUpdate) error {
 | 
				
			||||||
		if updates[i].MediaType == "" {
 | 
							if updates[i].MediaType == "" {
 | 
				
			||||||
			return errors.Errorf("update %d of %d passed to OCI1Index.UpdateInstances had no media type (was %q)", i+1, len(updates), index.Manifests[i].MediaType)
 | 
								return errors.Errorf("update %d of %d passed to OCI1Index.UpdateInstances had no media type (was %q)", i+1, len(updates), index.Manifests[i].MediaType)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err := SupportedOCI1MediaType(updates[i].MediaType); err != nil && SupportedSchema2MediaType(updates[i].MediaType) != nil && updates[i].MediaType != imgspecv1.MediaTypeImageIndex {
 | 
					 | 
				
			||||||
			return errors.Wrapf(err, "update %d of %d passed to OCI1Index.UpdateInstances had an unsupported media type (was %q): %q", i+1, len(updates), index.Manifests[i].MediaType, updates[i].MediaType)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		index.Manifests[i].MediaType = updates[i].MediaType
 | 
							index.Manifests[i].MediaType = updates[i].MediaType
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
| 
						 | 
					@ -75,26 +73,31 @@ func (index *OCI1Index) UpdateInstances(updates []ListUpdate) error {
 | 
				
			||||||
// ChooseInstance parses blob as an oci v1 manifest index, and returns the digest
 | 
					// ChooseInstance parses blob as an oci v1 manifest index, and returns the digest
 | 
				
			||||||
// of the image which is appropriate for the current environment.
 | 
					// of the image which is appropriate for the current environment.
 | 
				
			||||||
func (index *OCI1Index) ChooseInstance(ctx *types.SystemContext) (digest.Digest, error) {
 | 
					func (index *OCI1Index) ChooseInstance(ctx *types.SystemContext) (digest.Digest, error) {
 | 
				
			||||||
	wantedArch := runtime.GOARCH
 | 
						wantedPlatforms, err := platform.WantedPlatforms(ctx)
 | 
				
			||||||
	if ctx != nil && ctx.ArchitectureChoice != "" {
 | 
						if err != nil {
 | 
				
			||||||
		wantedArch = ctx.ArchitectureChoice
 | 
							return "", errors.Wrapf(err, "error getting platform information %#v", ctx)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	wantedOS := runtime.GOOS
 | 
						for _, wantedPlatform := range wantedPlatforms {
 | 
				
			||||||
	if ctx != nil && ctx.OSChoice != "" {
 | 
					 | 
				
			||||||
		wantedOS = ctx.OSChoice
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for _, d := range index.Manifests {
 | 
							for _, d := range index.Manifests {
 | 
				
			||||||
		if d.Platform != nil && d.Platform.Architecture == wantedArch && d.Platform.OS == wantedOS {
 | 
								imagePlatform := imgspecv1.Platform{
 | 
				
			||||||
 | 
									Architecture: d.Platform.Architecture,
 | 
				
			||||||
 | 
									OS:           d.Platform.OS,
 | 
				
			||||||
 | 
									OSVersion:    d.Platform.OSVersion,
 | 
				
			||||||
 | 
									OSFeatures:   dupStringSlice(d.Platform.OSFeatures),
 | 
				
			||||||
 | 
									Variant:      d.Platform.Variant,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if platform.MatchesPlatform(imagePlatform, wantedPlatform) {
 | 
				
			||||||
				return d.Digest, nil
 | 
									return d.Digest, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, d := range index.Manifests {
 | 
						for _, d := range index.Manifests {
 | 
				
			||||||
		if d.Platform == nil {
 | 
							if d.Platform == nil {
 | 
				
			||||||
			return d.Digest, nil
 | 
								return d.Digest, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return "", fmt.Errorf("no image found in image index for architecture %s, OS %s", wantedArch, wantedOS)
 | 
						return "", fmt.Errorf("no image found in image index for architecture %s, variant %s, OS %s", wantedPlatforms[0].Architecture, wantedPlatforms[0].Variant, wantedPlatforms[0].OS)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serialize returns the index in a blob format.
 | 
					// Serialize returns the index in a blob format.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,7 +65,7 @@ type restConfig struct {
 | 
				
			||||||
	BearerToken string
 | 
						BearerToken string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TLSClientConfig contains settings to enable transport layer security
 | 
						// TLSClientConfig contains settings to enable transport layer security
 | 
				
			||||||
	restTLSClientConfig
 | 
						TLSClientConfig restTLSClientConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Server should be accessed without verifying the TLS
 | 
						// Server should be accessed without verifying the TLS
 | 
				
			||||||
	// certificate. For testing only.
 | 
						// certificate. For testing only.
 | 
				
			||||||
| 
						 | 
					@ -238,8 +238,8 @@ func getServerIdentificationPartialConfig(configAuthInfo clientcmdAuthInfo, conf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// configClusterInfo holds the information identify the server provided by .kubeconfig
 | 
						// configClusterInfo holds the information identify the server provided by .kubeconfig
 | 
				
			||||||
	configClientConfig := &restConfig{}
 | 
						configClientConfig := &restConfig{}
 | 
				
			||||||
	configClientConfig.CAFile = configClusterInfo.CertificateAuthority
 | 
						configClientConfig.TLSClientConfig.CAFile = configClusterInfo.CertificateAuthority
 | 
				
			||||||
	configClientConfig.CAData = configClusterInfo.CertificateAuthorityData
 | 
						configClientConfig.TLSClientConfig.CAData = configClusterInfo.CertificateAuthorityData
 | 
				
			||||||
	configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
 | 
						configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
 | 
				
			||||||
	if err := mergo.MergeWithOverwrite(mergedConfig, configClientConfig); err != nil {
 | 
						if err := mergo.MergeWithOverwrite(mergedConfig, configClientConfig); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
| 
						 | 
					@ -264,10 +264,10 @@ func getUserIdentificationPartialConfig(configAuthInfo clientcmdAuthInfo) (*rest
 | 
				
			||||||
		mergedConfig.BearerToken = configAuthInfo.Token
 | 
							mergedConfig.BearerToken = configAuthInfo.Token
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 {
 | 
						if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 {
 | 
				
			||||||
		mergedConfig.CertFile = configAuthInfo.ClientCertificate
 | 
							mergedConfig.TLSClientConfig.CertFile = configAuthInfo.ClientCertificate
 | 
				
			||||||
		mergedConfig.CertData = configAuthInfo.ClientCertificateData
 | 
							mergedConfig.TLSClientConfig.CertData = configAuthInfo.ClientCertificateData
 | 
				
			||||||
		mergedConfig.KeyFile = configAuthInfo.ClientKey
 | 
							mergedConfig.TLSClientConfig.KeyFile = configAuthInfo.ClientKey
 | 
				
			||||||
		mergedConfig.KeyData = configAuthInfo.ClientKeyData
 | 
							mergedConfig.TLSClientConfig.KeyData = configAuthInfo.ClientKeyData
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 {
 | 
						if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 {
 | 
				
			||||||
		mergedConfig.Username = configAuthInfo.Username
 | 
							mergedConfig.Username = configAuthInfo.Username
 | 
				
			||||||
| 
						 | 
					@ -806,8 +806,8 @@ func defaultServerURL(host string, defaultTLS bool) (*url.URL, error) {
 | 
				
			||||||
func defaultServerURLFor(config *restConfig) (*url.URL, error) {
 | 
					func defaultServerURLFor(config *restConfig) (*url.URL, error) {
 | 
				
			||||||
	// TODO: move the default to secure when the apiserver supports TLS by default
 | 
						// TODO: move the default to secure when the apiserver supports TLS by default
 | 
				
			||||||
	// config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA."
 | 
						// config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA."
 | 
				
			||||||
	hasCA := len(config.CAFile) != 0 || len(config.CAData) != 0
 | 
						hasCA := len(config.TLSClientConfig.CAFile) != 0 || len(config.TLSClientConfig.CAData) != 0
 | 
				
			||||||
	hasCert := len(config.CertFile) != 0 || len(config.CertData) != 0
 | 
						hasCert := len(config.TLSClientConfig.CertFile) != 0 || len(config.TLSClientConfig.CertData) != 0
 | 
				
			||||||
	defaultTLS := hasCA || hasCert || config.Insecure
 | 
						defaultTLS := hasCA || hasCert || config.Insecure
 | 
				
			||||||
	host := config.Host
 | 
						host := config.Host
 | 
				
			||||||
	if host == "" {
 | 
						if host == "" {
 | 
				
			||||||
| 
						 | 
					@ -968,11 +968,11 @@ func tlsConfigFor(c *restConfig) (*tls.Config, error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.HasCA() {
 | 
						if c.HasCA() {
 | 
				
			||||||
		tlsConfig.RootCAs = rootCertPool(c.CAData)
 | 
							tlsConfig.RootCAs = rootCertPool(c.TLSClientConfig.CAData)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.HasCertAuth() {
 | 
						if c.HasCertAuth() {
 | 
				
			||||||
		cert, err := tls.X509KeyPair(c.CertData, c.KeyData)
 | 
							cert, err := tls.X509KeyPair(c.TLSClientConfig.CertData, c.TLSClientConfig.KeyData)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -988,17 +988,17 @@ func tlsConfigFor(c *restConfig) (*tls.Config, error) {
 | 
				
			||||||
// either populated or were empty to start.
 | 
					// either populated or were empty to start.
 | 
				
			||||||
func loadTLSFiles(c *restConfig) error {
 | 
					func loadTLSFiles(c *restConfig) error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
 | 
						c.TLSClientConfig.CAData, err = dataFromSliceOrFile(c.TLSClientConfig.CAData, c.TLSClientConfig.CAFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
 | 
						c.TLSClientConfig.CertData, err = dataFromSliceOrFile(c.TLSClientConfig.CertData, c.TLSClientConfig.CertFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
 | 
						c.TLSClientConfig.KeyData, err = dataFromSliceOrFile(c.TLSClientConfig.KeyData, c.TLSClientConfig.KeyFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1042,13 +1042,13 @@ func rootCertPool(caData []byte) *x509.CertPool {
 | 
				
			||||||
// HasCA is a modified copy of k8s.io/kubernetes/pkg/client/transport.Config.HasCA.
 | 
					// HasCA is a modified copy of k8s.io/kubernetes/pkg/client/transport.Config.HasCA.
 | 
				
			||||||
// HasCA returns whether the configuration has a certificate authority or not.
 | 
					// HasCA returns whether the configuration has a certificate authority or not.
 | 
				
			||||||
func (c *restConfig) HasCA() bool {
 | 
					func (c *restConfig) HasCA() bool {
 | 
				
			||||||
	return len(c.CAData) > 0 || len(c.CAFile) > 0
 | 
						return len(c.TLSClientConfig.CAData) > 0 || len(c.TLSClientConfig.CAFile) > 0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HasCertAuth is a modified copy of k8s.io/kubernetes/pkg/client/transport.Config.HasCertAuth.
 | 
					// HasCertAuth is a modified copy of k8s.io/kubernetes/pkg/client/transport.Config.HasCertAuth.
 | 
				
			||||||
// HasCertAuth returns whether the configuration has certificate authentication or not.
 | 
					// HasCertAuth returns whether the configuration has certificate authentication or not.
 | 
				
			||||||
func (c *restConfig) HasCertAuth() bool {
 | 
					func (c *restConfig) HasCertAuth() bool {
 | 
				
			||||||
	return len(c.CertData) != 0 || len(c.CertFile) != 0
 | 
						return len(c.TLSClientConfig.CertData) != 0 || len(c.TLSClientConfig.CertFile) != 0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// clientcmdConfig is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.Config.
 | 
					// clientcmdConfig is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.Config.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,9 +9,9 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/containers/image/v5/pkg/blobinfocache/internal/prioritize"
 | 
						"github.com/containers/image/v5/pkg/blobinfocache/internal/prioritize"
 | 
				
			||||||
	"github.com/containers/image/v5/types"
 | 
						"github.com/containers/image/v5/types"
 | 
				
			||||||
	bolt "github.com/etcd-io/bbolt"
 | 
					 | 
				
			||||||
	"github.com/opencontainers/go-digest"
 | 
						"github.com/opencontainers/go-digest"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						bolt "go.etcd.io/bbolt"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type dockerAuthConfig struct {
 | 
					type dockerAuthConfig struct {
 | 
				
			||||||
	Auth          string `json:"auth,omitempty"`
 | 
						Auth          string `json:"auth,omitempty"`
 | 
				
			||||||
 | 
						IdentityToken string `json:"identitytoken,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type dockerConfigFile struct {
 | 
					type dockerConfigFile struct {
 | 
				
			||||||
| 
						 | 
					@ -72,20 +73,23 @@ func SetAuthentication(sys *types.SystemContext, registry, username, password st
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetAuthentication returns the registry credentials stored in
 | 
					// GetCredentials returns the registry credentials stored in either auth.json
 | 
				
			||||||
// either auth.json file or .docker/config.json
 | 
					// file or .docker/config.json, including support for OAuth2 and IdentityToken.
 | 
				
			||||||
// If an entry is not found empty strings are returned for the username and password
 | 
					// If an entry is not found, an empty struct is returned.
 | 
				
			||||||
func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) {
 | 
					func GetCredentials(sys *types.SystemContext, registry string) (types.DockerAuthConfig, error) {
 | 
				
			||||||
	if sys != nil && sys.DockerAuthConfig != nil {
 | 
						if sys != nil && sys.DockerAuthConfig != nil {
 | 
				
			||||||
		logrus.Debug("Returning credentials from DockerAuthConfig")
 | 
							logrus.Debug("Returning credentials from DockerAuthConfig")
 | 
				
			||||||
		return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil
 | 
							return *sys.DockerAuthConfig, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if enableKeyring {
 | 
						if enableKeyring {
 | 
				
			||||||
		username, password, err := getAuthFromKernelKeyring(registry)
 | 
							username, password, err := getAuthFromKernelKeyring(registry)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			logrus.Debug("returning credentials from kernel keyring")
 | 
								logrus.Debug("returning credentials from kernel keyring")
 | 
				
			||||||
			return username, password, nil
 | 
								return types.DockerAuthConfig{
 | 
				
			||||||
 | 
									Username: username,
 | 
				
			||||||
 | 
									Password: password,
 | 
				
			||||||
 | 
								}, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,18 +108,39 @@ func GetAuthentication(sys *types.SystemContext, registry string) (string, strin
 | 
				
			||||||
		authPath{path: filepath.Join(homedir.Get(), dockerLegacyHomePath), legacyFormat: true})
 | 
							authPath{path: filepath.Join(homedir.Get(), dockerLegacyHomePath), legacyFormat: true})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, path := range paths {
 | 
						for _, path := range paths {
 | 
				
			||||||
		username, password, err := findAuthentication(registry, path.path, path.legacyFormat)
 | 
							authConfig, err := findAuthentication(registry, path.path, path.legacyFormat)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			logrus.Debugf("Credentials not found")
 | 
								logrus.Debugf("Credentials not found")
 | 
				
			||||||
 | 
								return types.DockerAuthConfig{}, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (authConfig.Username != "" && authConfig.Password != "") || authConfig.IdentityToken != "" {
 | 
				
			||||||
 | 
								logrus.Debugf("Returning credentials from %s", path.path)
 | 
				
			||||||
 | 
								return authConfig, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logrus.Debugf("Credentials not found")
 | 
				
			||||||
 | 
						return types.DockerAuthConfig{}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAuthentication returns the registry credentials stored in
 | 
				
			||||||
 | 
					// either auth.json file or .docker/config.json
 | 
				
			||||||
 | 
					// If an entry is not found empty strings are returned for the username and password
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Deprecated: This API only has support for username and password. To get the
 | 
				
			||||||
 | 
					// support for oauth2 in docker registry authentication, we added the new
 | 
				
			||||||
 | 
					// GetCredentials API. The new API should be used and this API is kept to
 | 
				
			||||||
 | 
					// maintain backward compatibility.
 | 
				
			||||||
 | 
					func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) {
 | 
				
			||||||
 | 
						auth, err := GetCredentials(sys, registry)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
		return "", "", err
 | 
							return "", "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		if username != "" && password != "" {
 | 
						if auth.IdentityToken != "" {
 | 
				
			||||||
			logrus.Debugf("Returning credentials from %s", path.path)
 | 
							return "", "", errors.Wrap(ErrNotSupported, "non-empty identity token found and this API doesn't support it")
 | 
				
			||||||
			return username, password, nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
						return auth.Username, auth.Password, nil
 | 
				
			||||||
	logrus.Debugf("Credentials not found")
 | 
					 | 
				
			||||||
	return "", "", nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RemoveAuthentication deletes the credentials stored in auth.json
 | 
					// RemoveAuthentication deletes the credentials stored in auth.json
 | 
				
			||||||
| 
						 | 
					@ -294,20 +319,28 @@ func deleteAuthFromCredHelper(credHelper, registry string) error {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// findAuthentication looks for auth of registry in path
 | 
					// findAuthentication looks for auth of registry in path
 | 
				
			||||||
func findAuthentication(registry, path string, legacyFormat bool) (string, string, error) {
 | 
					func findAuthentication(registry, path string, legacyFormat bool) (types.DockerAuthConfig, error) {
 | 
				
			||||||
	auths, err := readJSONFile(path, legacyFormat)
 | 
						auths, err := readJSONFile(path, legacyFormat)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", "", errors.Wrapf(err, "error reading JSON file %q", path)
 | 
							return types.DockerAuthConfig{}, errors.Wrapf(err, "error reading JSON file %q", path)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// First try cred helpers. They should always be normalized.
 | 
						// First try cred helpers. They should always be normalized.
 | 
				
			||||||
	if ch, exists := auths.CredHelpers[registry]; exists {
 | 
						if ch, exists := auths.CredHelpers[registry]; exists {
 | 
				
			||||||
		return getAuthFromCredHelper(ch, registry)
 | 
							username, password, err := getAuthFromCredHelper(ch, registry)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return types.DockerAuthConfig{}, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return types.DockerAuthConfig{
 | 
				
			||||||
 | 
								Username: username,
 | 
				
			||||||
 | 
								Password: password,
 | 
				
			||||||
 | 
							}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// I'm feeling lucky
 | 
						// I'm feeling lucky
 | 
				
			||||||
	if val, exists := auths.AuthConfigs[registry]; exists {
 | 
						if val, exists := auths.AuthConfigs[registry]; exists {
 | 
				
			||||||
		return decodeDockerAuth(val.Auth)
 | 
							return decodeDockerAuth(val)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// bad luck; let's normalize the entries first
 | 
						// bad luck; let's normalize the entries first
 | 
				
			||||||
| 
						 | 
					@ -316,25 +349,35 @@ func findAuthentication(registry, path string, legacyFormat bool) (string, strin
 | 
				
			||||||
	for k, v := range auths.AuthConfigs {
 | 
						for k, v := range auths.AuthConfigs {
 | 
				
			||||||
		normalizedAuths[normalizeRegistry(k)] = v
 | 
							normalizedAuths[normalizeRegistry(k)] = v
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if val, exists := normalizedAuths[registry]; exists {
 | 
						if val, exists := normalizedAuths[registry]; exists {
 | 
				
			||||||
		return decodeDockerAuth(val.Auth)
 | 
							return decodeDockerAuth(val)
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return "", "", nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func decodeDockerAuth(s string) (string, string, error) {
 | 
						return types.DockerAuthConfig{}, nil
 | 
				
			||||||
	decoded, err := base64.StdEncoding.DecodeString(s)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", "", err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// decodeDockerAuth decodes the username and password, which is
 | 
				
			||||||
 | 
					// encoded in base64.
 | 
				
			||||||
 | 
					func decodeDockerAuth(conf dockerAuthConfig) (types.DockerAuthConfig, error) {
 | 
				
			||||||
 | 
						decoded, err := base64.StdEncoding.DecodeString(conf.Auth)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return types.DockerAuthConfig{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	parts := strings.SplitN(string(decoded), ":", 2)
 | 
						parts := strings.SplitN(string(decoded), ":", 2)
 | 
				
			||||||
	if len(parts) != 2 {
 | 
						if len(parts) != 2 {
 | 
				
			||||||
		// if it's invalid just skip, as docker does
 | 
							// if it's invalid just skip, as docker does
 | 
				
			||||||
		return "", "", nil
 | 
							return types.DockerAuthConfig{}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	user := parts[0]
 | 
						user := parts[0]
 | 
				
			||||||
	password := strings.Trim(parts[1], "\x00")
 | 
						password := strings.Trim(parts[1], "\x00")
 | 
				
			||||||
	return user, password, nil
 | 
						return types.DockerAuthConfig{
 | 
				
			||||||
 | 
							Username:      user,
 | 
				
			||||||
 | 
							Password:      password,
 | 
				
			||||||
 | 
							IdentityToken: conf.IdentityToken,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// convertToHostname converts a registry url which has http|https prepended
 | 
					// convertToHostname converts a registry url which has http|https prepended
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										269
									
								
								vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										269
									
								
								vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
					@ -2,16 +2,17 @@ package sysregistriesv2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/BurntSushi/toml"
 | 
						"github.com/BurntSushi/toml"
 | 
				
			||||||
	"github.com/containers/image/v5/docker/reference"
 | 
						"github.com/containers/image/v5/docker/reference"
 | 
				
			||||||
	"github.com/containers/image/v5/types"
 | 
						"github.com/containers/image/v5/types"
 | 
				
			||||||
 | 
						"github.com/containers/storage/pkg/homedir"
 | 
				
			||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,16 @@ var systemRegistriesConfPath = builtinRegistriesConfPath
 | 
				
			||||||
// DO NOT change this, instead see systemRegistriesConfPath above.
 | 
					// DO NOT change this, instead see systemRegistriesConfPath above.
 | 
				
			||||||
const builtinRegistriesConfPath = "/etc/containers/registries.conf"
 | 
					const builtinRegistriesConfPath = "/etc/containers/registries.conf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// systemRegistriesConfDirPath is the path to the system-wide registry
 | 
				
			||||||
 | 
					// configuration directory and is used to add/subtract potential registries for
 | 
				
			||||||
 | 
					// obtaining images.  You can override this at build time with
 | 
				
			||||||
 | 
					// -ldflags '-X github.com/containers/image/sysregistries.systemRegistriesConfDirecotyPath=$your_path'
 | 
				
			||||||
 | 
					var systemRegistriesConfDirPath = builtinRegistriesConfDirPath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// builtinRegistriesConfDirPath is the path to the registry configuration directory.
 | 
				
			||||||
 | 
					// DO NOT change this, instead see systemRegistriesConfDirectoryPath above.
 | 
				
			||||||
 | 
					const builtinRegistriesConfDirPath = "/etc/containers/registries.conf.d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Endpoint describes a remote location of a registry.
 | 
					// Endpoint describes a remote location of a registry.
 | 
				
			||||||
type Endpoint struct {
 | 
					type Endpoint struct {
 | 
				
			||||||
	// The endpoint's remote location.
 | 
						// The endpoint's remote location.
 | 
				
			||||||
| 
						 | 
					@ -35,6 +46,12 @@ type Endpoint struct {
 | 
				
			||||||
	Insecure bool `toml:"insecure,omitempty"`
 | 
						Insecure bool `toml:"insecure,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// userRegistriesFile is the path to the per user registry configuration file.
 | 
				
			||||||
 | 
					var userRegistriesFile = filepath.FromSlash(".config/containers/registries.conf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// userRegistriesDir is the path to the per user registry configuration file.
 | 
				
			||||||
 | 
					var userRegistriesDir = filepath.FromSlash(".config/containers/registries.conf.d")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// rewriteReference will substitute the provided reference `prefix` to the
 | 
					// rewriteReference will substitute the provided reference `prefix` to the
 | 
				
			||||||
// endpoints `location` from the `ref` and creates a new named reference from it.
 | 
					// endpoints `location` from the `ref` and creates a new named reference from it.
 | 
				
			||||||
// The function errors if the newly created reference is not parsable.
 | 
					// The function errors if the newly created reference is not parsable.
 | 
				
			||||||
| 
						 | 
					@ -49,7 +66,7 @@ func (e *Endpoint) rewriteReference(ref reference.Named, prefix string) (referen
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.Wrapf(err, "error rewriting reference")
 | 
							return nil, errors.Wrapf(err, "error rewriting reference")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	logrus.Debugf("reference rewritten from '%v' to '%v'", refString, newParsedRef.String())
 | 
					
 | 
				
			||||||
	return newParsedRef, nil
 | 
						return newParsedRef, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -302,29 +319,83 @@ func (config *V2RegistriesConf) postProcess() error {
 | 
				
			||||||
		config.UnqualifiedSearchRegistries[i] = registry
 | 
							config.UnqualifiedSearchRegistries[i] = registry
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Registries are ordered and the first longest prefix always wins,
 | 
				
			||||||
 | 
						// rendering later items with the same prefix non-existent. We cannot error
 | 
				
			||||||
 | 
						// out anymore as this might break existing users, so let's just ignore them
 | 
				
			||||||
 | 
						// to guarantee that the same prefix exists only once.
 | 
				
			||||||
 | 
						knownPrefixes := make(map[string]bool)
 | 
				
			||||||
 | 
						uniqueRegistries := []Registry{}
 | 
				
			||||||
 | 
						for i := range config.Registries {
 | 
				
			||||||
 | 
							// TODO: should we warn if we see the same prefix being used multiple times?
 | 
				
			||||||
 | 
							if _, exists := knownPrefixes[config.Registries[i].Prefix]; !exists {
 | 
				
			||||||
 | 
								knownPrefixes[config.Registries[i].Prefix] = true
 | 
				
			||||||
 | 
								uniqueRegistries = append(uniqueRegistries, config.Registries[i])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.Registries = uniqueRegistries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConfigPath returns the path to the system-wide registry configuration file.
 | 
					// ConfigPath returns the path to the system-wide registry configuration file.
 | 
				
			||||||
func ConfigPath(ctx *types.SystemContext) string {
 | 
					func ConfigPath(ctx *types.SystemContext) string {
 | 
				
			||||||
	confPath := systemRegistriesConfPath
 | 
						if ctx != nil && ctx.SystemRegistriesConfPath != "" {
 | 
				
			||||||
	if ctx != nil {
 | 
							return ctx.SystemRegistriesConfPath
 | 
				
			||||||
		if ctx.SystemRegistriesConfPath != "" {
 | 
					 | 
				
			||||||
			confPath = ctx.SystemRegistriesConfPath
 | 
					 | 
				
			||||||
		} else if ctx.RootForImplicitAbsolutePaths != "" {
 | 
					 | 
				
			||||||
			confPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfPath)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userRegistriesFilePath := filepath.Join(homedir.Get(), userRegistriesFile)
 | 
				
			||||||
 | 
						if _, err := os.Stat(userRegistriesFilePath); err == nil {
 | 
				
			||||||
 | 
							return userRegistriesFilePath
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
 | 
				
			||||||
 | 
							return filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfPath)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return systemRegistriesConfPath
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ConfigDirPath returns the path to the system-wide directory for drop-in
 | 
				
			||||||
 | 
					// registry configuration files.
 | 
				
			||||||
 | 
					func ConfigDirPath(ctx *types.SystemContext) string {
 | 
				
			||||||
 | 
						if ctx != nil && ctx.SystemRegistriesConfDirPath != "" {
 | 
				
			||||||
 | 
							return ctx.SystemRegistriesConfDirPath
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userRegistriesDirPath := filepath.Join(homedir.Get(), userRegistriesDir)
 | 
				
			||||||
 | 
						if _, err := os.Stat(userRegistriesDirPath); err == nil {
 | 
				
			||||||
 | 
							return userRegistriesDirPath
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
 | 
				
			||||||
 | 
							return filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfDirPath)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return systemRegistriesConfDirPath
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// configWrapper is used to store the paths from ConfigPath and ConfigDirPath
 | 
				
			||||||
 | 
					// and acts as a key to the internal cache.
 | 
				
			||||||
 | 
					type configWrapper struct {
 | 
				
			||||||
 | 
						configPath    string
 | 
				
			||||||
 | 
						configDirPath string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// newConfigWrapper returns a configWrapper for the specified SystemContext.
 | 
				
			||||||
 | 
					func newConfigWrapper(ctx *types.SystemContext) configWrapper {
 | 
				
			||||||
 | 
						return configWrapper{
 | 
				
			||||||
 | 
							configPath:    ConfigPath(ctx),
 | 
				
			||||||
 | 
							configDirPath: ConfigDirPath(ctx),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return confPath
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// configMutex is used to synchronize concurrent accesses to configCache.
 | 
					// configMutex is used to synchronize concurrent accesses to configCache.
 | 
				
			||||||
var configMutex = sync.Mutex{}
 | 
					var configMutex = sync.Mutex{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// configCache caches already loaded configs with config paths as keys and is
 | 
					// configCache caches already loaded configs with config paths as keys and is
 | 
				
			||||||
// used to avoid redudantly parsing configs. Concurrent accesses to the cache
 | 
					// used to avoid redundantly parsing configs. Concurrent accesses to the cache
 | 
				
			||||||
// are synchronized via configMutex.
 | 
					// are synchronized via configMutex.
 | 
				
			||||||
var configCache = make(map[string]*V2RegistriesConf)
 | 
					var configCache = make(map[configWrapper]*V2RegistriesConf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InvalidateCache invalidates the registry cache.  This function is meant to be
 | 
					// InvalidateCache invalidates the registry cache.  This function is meant to be
 | 
				
			||||||
// used for long-running processes that need to reload potential changes made to
 | 
					// used for long-running processes that need to reload potential changes made to
 | 
				
			||||||
| 
						 | 
					@ -332,66 +403,108 @@ var configCache = make(map[string]*V2RegistriesConf)
 | 
				
			||||||
func InvalidateCache() {
 | 
					func InvalidateCache() {
 | 
				
			||||||
	configMutex.Lock()
 | 
						configMutex.Lock()
 | 
				
			||||||
	defer configMutex.Unlock()
 | 
						defer configMutex.Unlock()
 | 
				
			||||||
	configCache = make(map[string]*V2RegistriesConf)
 | 
						configCache = make(map[configWrapper]*V2RegistriesConf)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getConfig returns the config object corresponding to ctx, loading it if it is not yet cached.
 | 
					// getConfig returns the config object corresponding to ctx, loading it if it is not yet cached.
 | 
				
			||||||
func getConfig(ctx *types.SystemContext) (*V2RegistriesConf, error) {
 | 
					func getConfig(ctx *types.SystemContext) (*V2RegistriesConf, error) {
 | 
				
			||||||
	configPath := ConfigPath(ctx)
 | 
						wrapper := newConfigWrapper(ctx)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	configMutex.Lock()
 | 
						configMutex.Lock()
 | 
				
			||||||
	// if the config has already been loaded, return the cached registries
 | 
						if config, inCache := configCache[wrapper]; inCache {
 | 
				
			||||||
	if config, inCache := configCache[configPath]; inCache {
 | 
					 | 
				
			||||||
		configMutex.Unlock()
 | 
							configMutex.Unlock()
 | 
				
			||||||
		return config, nil
 | 
							return config, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	configMutex.Unlock()
 | 
						configMutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return TryUpdatingCache(ctx)
 | 
						return tryUpdatingCache(ctx, wrapper)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// dropInConfigs returns a slice of drop-in-configs from the registries.conf.d
 | 
				
			||||||
 | 
					// directory.
 | 
				
			||||||
 | 
					func dropInConfigs(wrapper configWrapper) ([]string, error) {
 | 
				
			||||||
 | 
						var configs []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := filepath.Walk(wrapper.configDirPath,
 | 
				
			||||||
 | 
							// WalkFunc to read additional configs
 | 
				
			||||||
 | 
							func(path string, info os.FileInfo, err error) error {
 | 
				
			||||||
 | 
								switch {
 | 
				
			||||||
 | 
								case err != nil:
 | 
				
			||||||
 | 
									// return error (could be a permission problem)
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								case info == nil:
 | 
				
			||||||
 | 
									// this should only happen when err != nil but let's be sure
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								case info.IsDir():
 | 
				
			||||||
 | 
									if path != wrapper.configDirPath {
 | 
				
			||||||
 | 
										// make sure to not recurse into sub-directories
 | 
				
			||||||
 | 
										return filepath.SkipDir
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									// ignore directories
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									// only add *.conf files
 | 
				
			||||||
 | 
									if strings.HasSuffix(path, ".conf") {
 | 
				
			||||||
 | 
										configs = append(configs, path)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil && !os.IsNotExist(err) {
 | 
				
			||||||
 | 
							// Ignore IsNotExist errors: most systems won't have a registries.conf.d
 | 
				
			||||||
 | 
							// directory.
 | 
				
			||||||
 | 
							return nil, errors.Wrapf(err, "error reading registries.conf.d")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return configs, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TryUpdatingCache loads the configuration from the provided `SystemContext`
 | 
					// TryUpdatingCache loads the configuration from the provided `SystemContext`
 | 
				
			||||||
// without using the internal cache. On success, the loaded configuration will
 | 
					// without using the internal cache. On success, the loaded configuration will
 | 
				
			||||||
// be added into the internal registry cache.
 | 
					// be added into the internal registry cache.
 | 
				
			||||||
func TryUpdatingCache(ctx *types.SystemContext) (*V2RegistriesConf, error) {
 | 
					func TryUpdatingCache(ctx *types.SystemContext) (*V2RegistriesConf, error) {
 | 
				
			||||||
	configPath := ConfigPath(ctx)
 | 
						return tryUpdatingCache(ctx, newConfigWrapper(ctx))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// tryUpdatingCache implements TryUpdatingCache with an additional configWrapper
 | 
				
			||||||
 | 
					// argument to avoid redundantly calculating the config paths.
 | 
				
			||||||
 | 
					func tryUpdatingCache(ctx *types.SystemContext, wrapper configWrapper) (*V2RegistriesConf, error) {
 | 
				
			||||||
	configMutex.Lock()
 | 
						configMutex.Lock()
 | 
				
			||||||
	defer configMutex.Unlock()
 | 
						defer configMutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// load the config
 | 
						// load the config
 | 
				
			||||||
	config, err := loadRegistryConf(configPath)
 | 
						config := &tomlConfig{}
 | 
				
			||||||
	if err != nil {
 | 
						if err := config.loadConfig(wrapper.configPath, false); err != nil {
 | 
				
			||||||
		// Return an empty []Registry if we use the default config,
 | 
							// Continue with an empty []Registry if we use the default config, which
 | 
				
			||||||
		// which implies that the config path of the SystemContext
 | 
							// implies that the config path of the SystemContext isn't set.
 | 
				
			||||||
		// isn't set.  Note: if ctx.SystemRegistriesConfPath points to
 | 
							//
 | 
				
			||||||
		// the default config, we will still return an error.
 | 
							// Note: if ctx.SystemRegistriesConfPath points to the default config,
 | 
				
			||||||
 | 
							// we will still return an error.
 | 
				
			||||||
		if os.IsNotExist(err) && (ctx == nil || ctx.SystemRegistriesConfPath == "") {
 | 
							if os.IsNotExist(err) && (ctx == nil || ctx.SystemRegistriesConfPath == "") {
 | 
				
			||||||
			return &V2RegistriesConf{Registries: []Registry{}}, nil
 | 
								config = &tomlConfig{}
 | 
				
			||||||
 | 
								config.V2RegistriesConf = V2RegistriesConf{Registries: []Registry{}}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return nil, errors.Wrapf(err, "error loading registries configuration %q", wrapper.configPath)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Load the configs from the conf directory path.
 | 
				
			||||||
 | 
						dinConfigs, err := dropInConfigs(wrapper)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						for _, path := range dinConfigs {
 | 
				
			||||||
 | 
							// Enforce v2 format for drop-in-configs.
 | 
				
			||||||
 | 
							if err := config.loadConfig(path, true); err != nil {
 | 
				
			||||||
 | 
								return nil, errors.Wrapf(err, "error loading drop-in registries configuration %q", path)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v2Config := &config.V2RegistriesConf
 | 
						v2Config := &config.V2RegistriesConf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// backwards compatibility for v1 configs
 | 
					 | 
				
			||||||
	if config.V1RegistriesConf.Nonempty() {
 | 
					 | 
				
			||||||
		if config.V2RegistriesConf.Nonempty() {
 | 
					 | 
				
			||||||
			return nil, &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		v2, err := config.V1RegistriesConf.ConvertToV2()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		v2Config = v2
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := v2Config.postProcess(); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// populate the cache
 | 
						// populate the cache
 | 
				
			||||||
	configCache[configPath] = v2Config
 | 
						configCache[wrapper] = v2Config
 | 
				
			||||||
	return v2Config, nil
 | 
						return v2Config, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -470,16 +583,72 @@ func FindRegistry(ctx *types.SystemContext, ref string) (*Registry, error) {
 | 
				
			||||||
	return nil, nil
 | 
						return nil, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Loads the registry configuration file from the filesystem and then unmarshals
 | 
					// loadConfig loads and unmarshals the configuration at the specified path. Note
 | 
				
			||||||
// it.  Returns the unmarshalled object.
 | 
					// that v1 configs are translated into v2 and are cleared.  Use forceV2 if the
 | 
				
			||||||
func loadRegistryConf(configPath string) (*tomlConfig, error) {
 | 
					// config must in the v2 format.
 | 
				
			||||||
	config := &tomlConfig{}
 | 
					//
 | 
				
			||||||
 | 
					// Note that specified fields in path will replace already set fields in the
 | 
				
			||||||
 | 
					// tomlConfig.  Only the [[registry]] tables are merged by prefix.
 | 
				
			||||||
 | 
					func (c *tomlConfig) loadConfig(path string, forceV2 bool) error {
 | 
				
			||||||
 | 
						logrus.Debugf("Loading registries configuration %q", path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	configBytes, err := ioutil.ReadFile(configPath)
 | 
						// Save the registries before decoding the file where they could be lost.
 | 
				
			||||||
 | 
						// We merge them later again.
 | 
				
			||||||
 | 
						registryMap := make(map[string]Registry)
 | 
				
			||||||
 | 
						for i := range c.Registries {
 | 
				
			||||||
 | 
							registryMap[c.Registries[i].Prefix] = c.Registries[i]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Load the tomlConfig. Note that `DecodeFile` will overwrite set fields.
 | 
				
			||||||
 | 
						c.Registries = nil // important to clear the memory to prevent us from overlapping fields
 | 
				
			||||||
 | 
						_, err := toml.DecodeFile(path, c)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = toml.Unmarshal(configBytes, &config)
 | 
						if c.V1RegistriesConf.Nonempty() {
 | 
				
			||||||
	return config, err
 | 
							// Enforce the v2 format if requested.
 | 
				
			||||||
 | 
							if forceV2 {
 | 
				
			||||||
 | 
								return &InvalidRegistries{s: "registry must be in v2 format but is in v1"}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Convert a v1 config into a v2 config.
 | 
				
			||||||
 | 
							if c.V2RegistriesConf.Nonempty() {
 | 
				
			||||||
 | 
								return &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							v2, err := c.V1RegistriesConf.ConvertToV2()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							c.V1RegistriesConf = V1RegistriesConf{}
 | 
				
			||||||
 | 
							c.V2RegistriesConf = *v2
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Post process registries, set the correct prefixes, sanity checks, etc.
 | 
				
			||||||
 | 
						if err := c.postProcess(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Merge the freshly loaded registries.
 | 
				
			||||||
 | 
						for i := range c.Registries {
 | 
				
			||||||
 | 
							registryMap[c.Registries[i].Prefix] = c.Registries[i]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Go maps have a non-deterministic order when iterating the keys, so
 | 
				
			||||||
 | 
						// we dump them in a slice and sort it to enforce some order in
 | 
				
			||||||
 | 
						// Registries slice.  Some consumers of c/image (e.g., CRI-O) log the
 | 
				
			||||||
 | 
						// the configuration where a non-deterministic order could easily cause
 | 
				
			||||||
 | 
						// confusion.
 | 
				
			||||||
 | 
						prefixes := []string{}
 | 
				
			||||||
 | 
						for prefix := range registryMap {
 | 
				
			||||||
 | 
							prefixes = append(prefixes, prefix)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sort.Strings(prefixes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Registries = []Registry{}
 | 
				
			||||||
 | 
						for _, prefix := range prefixes {
 | 
				
			||||||
 | 
							c.Registries = append(c.Registries, registryMap[prefix])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,14 @@ func newReference(transport storageTransport, named reference.Named, id string)
 | 
				
			||||||
	if named == nil && id == "" {
 | 
						if named == nil && id == "" {
 | 
				
			||||||
		return nil, ErrInvalidReference
 | 
							return nil, ErrInvalidReference
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if named != nil && reference.IsNameOnly(named) {
 | 
				
			||||||
 | 
							return nil, errors.Wrapf(ErrInvalidReference, "reference %s has neither a tag nor a digest", named.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if id != "" {
 | 
				
			||||||
 | 
							if err := validateImageID(id); err != nil {
 | 
				
			||||||
 | 
								return nil, errors.Wrapf(ErrInvalidReference, "invalid ID value %q: %v", id, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// We take a copy of the transport, which contains a pointer to the
 | 
						// We take a copy of the transport, which contains a pointer to the
 | 
				
			||||||
	// store that it used for resolving this reference, so that the
 | 
						// store that it used for resolving this reference, so that the
 | 
				
			||||||
	// transport that we'll return from Transport() won't be affected by
 | 
						// transport that we'll return from Transport() won't be affected by
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,8 @@ type StoreTransport interface {
 | 
				
			||||||
	types.ImageTransport
 | 
						types.ImageTransport
 | 
				
			||||||
	// SetStore sets the default store for this transport.
 | 
						// SetStore sets the default store for this transport.
 | 
				
			||||||
	SetStore(storage.Store)
 | 
						SetStore(storage.Store)
 | 
				
			||||||
 | 
						// GetStoreIfSet returns the default store for this transport, or nil if not set/determined yet.
 | 
				
			||||||
 | 
						GetStoreIfSet() storage.Store
 | 
				
			||||||
	// GetImage retrieves the image from the transport's store that's named
 | 
						// GetImage retrieves the image from the transport's store that's named
 | 
				
			||||||
	// by the reference.
 | 
						// by the reference.
 | 
				
			||||||
	GetImage(types.ImageReference) (*storage.Image, error)
 | 
						GetImage(types.ImageReference) (*storage.Image, error)
 | 
				
			||||||
| 
						 | 
					@ -52,6 +54,9 @@ type StoreTransport interface {
 | 
				
			||||||
	// ParseStoreReference parses a reference, overriding any store
 | 
						// ParseStoreReference parses a reference, overriding any store
 | 
				
			||||||
	// specification that it may contain.
 | 
						// specification that it may contain.
 | 
				
			||||||
	ParseStoreReference(store storage.Store, reference string) (*storageReference, error)
 | 
						ParseStoreReference(store storage.Store, reference string) (*storageReference, error)
 | 
				
			||||||
 | 
						// NewStoreReference creates a reference for (named@ID) in store.
 | 
				
			||||||
 | 
						// either of name or ID can be unset; named must not be a reference.IsNameOnly.
 | 
				
			||||||
 | 
						NewStoreReference(store storage.Store, named reference.Named, id string) (*storageReference, error)
 | 
				
			||||||
	// SetDefaultUIDMap sets the default UID map to use when opening stores.
 | 
						// SetDefaultUIDMap sets the default UID map to use when opening stores.
 | 
				
			||||||
	SetDefaultUIDMap(idmap []idtools.IDMap)
 | 
						SetDefaultUIDMap(idmap []idtools.IDMap)
 | 
				
			||||||
	// SetDefaultGIDMap sets the default GID map to use when opening stores.
 | 
						// SetDefaultGIDMap sets the default GID map to use when opening stores.
 | 
				
			||||||
| 
						 | 
					@ -82,6 +87,11 @@ func (s *storageTransport) SetStore(store storage.Store) {
 | 
				
			||||||
	s.store = store
 | 
						s.store = store
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetStoreIfSet returns the default store for this transport, as set using SetStore() or initialized by default, or nil if not set/determined yet.
 | 
				
			||||||
 | 
					func (s *storageTransport) GetStoreIfSet() storage.Store {
 | 
				
			||||||
 | 
						return s.store
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetDefaultUIDMap sets the default UID map to use when opening stores.
 | 
					// SetDefaultUIDMap sets the default UID map to use when opening stores.
 | 
				
			||||||
func (s *storageTransport) SetDefaultUIDMap(idmap []idtools.IDMap) {
 | 
					func (s *storageTransport) SetDefaultUIDMap(idmap []idtools.IDMap) {
 | 
				
			||||||
	s.defaultUIDMap = idmap
 | 
						s.defaultUIDMap = idmap
 | 
				
			||||||
| 
						 | 
					@ -129,7 +139,7 @@ func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (
 | 
				
			||||||
		// If it looks like a digest, leave it alone for now.
 | 
							// If it looks like a digest, leave it alone for now.
 | 
				
			||||||
		if _, err := digest.Parse(possibleID); err != nil {
 | 
							if _, err := digest.Parse(possibleID); err != nil {
 | 
				
			||||||
			// Otherwise…
 | 
								// Otherwise…
 | 
				
			||||||
			if idSum, err := digest.Parse("sha256:" + possibleID); err == nil && idSum.Validate() == nil {
 | 
								if err := validateImageID(possibleID); err == nil {
 | 
				
			||||||
				id = possibleID // … it is a full ID
 | 
									id = possibleID // … it is a full ID
 | 
				
			||||||
			} else if img, err := store.Image(possibleID); err == nil && img != nil && len(possibleID) >= minimumTruncatedIDLength && strings.HasPrefix(img.ID, possibleID) {
 | 
								} else if img, err := store.Image(possibleID); err == nil && img != nil && len(possibleID) >= minimumTruncatedIDLength && strings.HasPrefix(img.ID, possibleID) {
 | 
				
			||||||
				// … it is a truncated version of the ID of an image that's present in local storage,
 | 
									// … it is a truncated version of the ID of an image that's present in local storage,
 | 
				
			||||||
| 
						 | 
					@ -167,7 +177,7 @@ func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (
 | 
				
			||||||
		named = reference.TagNameOnly(named)
 | 
							named = reference.TagNameOnly(named)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result, err := newReference(storageTransport{store: store, defaultUIDMap: s.defaultUIDMap, defaultGIDMap: s.defaultGIDMap}, named, id)
 | 
						result, err := s.NewStoreReference(store, named, id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -175,6 +185,12 @@ func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (
 | 
				
			||||||
	return result, nil
 | 
						return result, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewStoreReference creates a reference for (named@ID) in store.
 | 
				
			||||||
 | 
					// either of name or ID can be unset; named must not be a reference.IsNameOnly.
 | 
				
			||||||
 | 
					func (s *storageTransport) NewStoreReference(store storage.Store, named reference.Named, id string) (*storageReference, error) {
 | 
				
			||||||
 | 
						return newReference(storageTransport{store: store, defaultUIDMap: s.defaultUIDMap, defaultGIDMap: s.defaultGIDMap}, named, id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *storageTransport) GetStore() (storage.Store, error) {
 | 
					func (s *storageTransport) GetStore() (storage.Store, error) {
 | 
				
			||||||
	// Return the transport's previously-set store.  If we don't have one
 | 
						// Return the transport's previously-set store.  If we don't have one
 | 
				
			||||||
	// of those, initialize one now.
 | 
						// of those, initialize one now.
 | 
				
			||||||
| 
						 | 
					@ -342,7 +358,7 @@ func (s storageTransport) ValidatePolicyConfigurationScope(scope string) error {
 | 
				
			||||||
	switch len(fields) {
 | 
						switch len(fields) {
 | 
				
			||||||
	case 1: // name only
 | 
						case 1: // name only
 | 
				
			||||||
	case 2: // name:tag@ID or name[:tag]@digest
 | 
						case 2: // name:tag@ID or name[:tag]@digest
 | 
				
			||||||
		if _, idErr := digest.Parse("sha256:" + fields[1]); idErr != nil {
 | 
							if idErr := validateImageID(fields[1]); idErr != nil {
 | 
				
			||||||
			if _, digestErr := digest.Parse(fields[1]); digestErr != nil {
 | 
								if _, digestErr := digest.Parse(fields[1]); digestErr != nil {
 | 
				
			||||||
				return fmt.Errorf("%v is neither a valid digest(%s) nor a valid ID(%s)", fields[1], digestErr.Error(), idErr.Error())
 | 
									return fmt.Errorf("%v is neither a valid digest(%s) nor a valid ID(%s)", fields[1], digestErr.Error(), idErr.Error())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -351,7 +367,7 @@ func (s storageTransport) ValidatePolicyConfigurationScope(scope string) error {
 | 
				
			||||||
		if _, err := digest.Parse(fields[1]); err != nil {
 | 
							if _, err := digest.Parse(fields[1]); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if _, err := digest.Parse("sha256:" + fields[2]); err != nil {
 | 
							if err := validateImageID(fields[2]); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	default: // Coverage: This should never happen
 | 
						default: // Coverage: This should never happen
 | 
				
			||||||
| 
						 | 
					@ -363,3 +379,9 @@ func (s storageTransport) ValidatePolicyConfigurationScope(scope string) error {
 | 
				
			||||||
	// are few semantically invalid strings.
 | 
						// are few semantically invalid strings.
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validateImageID returns nil if id is a valid (full) image ID, or an error
 | 
				
			||||||
 | 
					func validateImageID(id string) error {
 | 
				
			||||||
 | 
						_, err := digest.Parse("sha256:" + id)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,6 @@ type ConfigUpdater interface {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tarballReference struct {
 | 
					type tarballReference struct {
 | 
				
			||||||
	transport   types.ImageTransport
 | 
					 | 
				
			||||||
	config      imgspecv1.Image
 | 
						config      imgspecv1.Image
 | 
				
			||||||
	annotations map[string]string
 | 
						annotations map[string]string
 | 
				
			||||||
	filenames   []string
 | 
						filenames   []string
 | 
				
			||||||
| 
						 | 
					@ -43,7 +42,7 @@ func (r *tarballReference) ConfigUpdate(config imgspecv1.Image, annotations map[
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *tarballReference) Transport() types.ImageTransport {
 | 
					func (r *tarballReference) Transport() types.ImageTransport {
 | 
				
			||||||
	return r.transport
 | 
						return Transport
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *tarballReference) StringWithinTransport() string {
 | 
					func (r *tarballReference) StringWithinTransport() string {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,12 +48,21 @@ func (t *tarballTransport) ParseReference(reference string) (types.ImageReferenc
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		f.Close()
 | 
							f.Close()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ref := &tarballReference{
 | 
						return NewReference(filenames, stdin)
 | 
				
			||||||
		transport: t,
 | 
					 | 
				
			||||||
		filenames: filenames,
 | 
					 | 
				
			||||||
		stdin:     stdin,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
	return ref, nil
 | 
					
 | 
				
			||||||
 | 
					// NewReference creates a new "tarball:" reference for the listed fileNames.
 | 
				
			||||||
 | 
					// If any of the fileNames is "-", the contents of stdin are used instead.
 | 
				
			||||||
 | 
					func NewReference(fileNames []string, stdin []byte) (types.ImageReference, error) {
 | 
				
			||||||
 | 
						for _, path := range fileNames {
 | 
				
			||||||
 | 
							if strings.Contains(path, separator) {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("Invalid path %q: paths including the separator %q are not supported", path, separator)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &tarballReference{
 | 
				
			||||||
 | 
							filenames: fileNames,
 | 
				
			||||||
 | 
							stdin:     stdin,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *tarballTransport) ValidatePolicyConfigurationScope(scope string) error {
 | 
					func (t *tarballTransport) ValidatePolicyConfigurationScope(scope string) error {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -399,6 +399,10 @@ type Image interface {
 | 
				
			||||||
	// This does not change the state of the original Image object.
 | 
						// This does not change the state of the original Image object.
 | 
				
			||||||
	UpdatedImage(ctx context.Context, options ManifestUpdateOptions) (Image, error)
 | 
						UpdatedImage(ctx context.Context, options ManifestUpdateOptions) (Image, error)
 | 
				
			||||||
	// SupportsEncryption returns an indicator that the image supports encryption
 | 
						// SupportsEncryption returns an indicator that the image supports encryption
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Deprecated: Initially used to determine if a manifest can be copied from a source manifest type since
 | 
				
			||||||
 | 
						// the process of updating a manifest between different manifest types was to update then convert.
 | 
				
			||||||
 | 
						// This resulted in some fields in the update being lost. This has been fixed by: https://github.com/containers/image/pull/836
 | 
				
			||||||
	SupportsEncryption(ctx context.Context) bool
 | 
						SupportsEncryption(ctx context.Context) bool
 | 
				
			||||||
	// Size returns an approximation of the amount of disk space which is consumed by the image in its current
 | 
						// Size returns an approximation of the amount of disk space which is consumed by the image in its current
 | 
				
			||||||
	// location.  If the size is not known, -1 will be returned.
 | 
						// location.  If the size is not known, -1 will be returned.
 | 
				
			||||||
| 
						 | 
					@ -450,6 +454,11 @@ type ImageInspectInfo struct {
 | 
				
			||||||
type DockerAuthConfig struct {
 | 
					type DockerAuthConfig struct {
 | 
				
			||||||
	Username string
 | 
						Username string
 | 
				
			||||||
	Password string
 | 
						Password string
 | 
				
			||||||
 | 
						// IdentityToken can be used as an refresh_token in place of username and
 | 
				
			||||||
 | 
						// password to obtain the bearer/access token in oauth2 flow. If identity
 | 
				
			||||||
 | 
						// token is set, password should not be set.
 | 
				
			||||||
 | 
						// Ref: https://docs.docker.com/registry/spec/auth/oauth/
 | 
				
			||||||
 | 
						IdentityToken string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// OptionalBool is a boolean with an additional undefined value, which is meant
 | 
					// OptionalBool is a boolean with an additional undefined value, which is meant
 | 
				
			||||||
| 
						 | 
					@ -497,6 +506,8 @@ type SystemContext struct {
 | 
				
			||||||
	RegistriesDirPath string
 | 
						RegistriesDirPath string
 | 
				
			||||||
	// Path to the system-wide registries configuration file
 | 
						// Path to the system-wide registries configuration file
 | 
				
			||||||
	SystemRegistriesConfPath string
 | 
						SystemRegistriesConfPath string
 | 
				
			||||||
 | 
						// Path to the system-wide registries configuration directory
 | 
				
			||||||
 | 
						SystemRegistriesConfDirPath string
 | 
				
			||||||
	// If not "", overrides the default path for the authentication file, but only new format files
 | 
						// If not "", overrides the default path for the authentication file, but only new format files
 | 
				
			||||||
	AuthFilePath string
 | 
						AuthFilePath string
 | 
				
			||||||
	// if not "", overrides the default path for the authentication file, but with the legacy format;
 | 
						// if not "", overrides the default path for the authentication file, but with the legacy format;
 | 
				
			||||||
| 
						 | 
					@ -510,6 +521,8 @@ type SystemContext struct {
 | 
				
			||||||
	ArchitectureChoice string
 | 
						ArchitectureChoice string
 | 
				
			||||||
	// If not "", overrides the use of platform.GOOS when choosing an image or verifying OS match.
 | 
						// If not "", overrides the use of platform.GOOS when choosing an image or verifying OS match.
 | 
				
			||||||
	OSChoice string
 | 
						OSChoice string
 | 
				
			||||||
 | 
						// If not "", overrides the use of detected ARM platform variant when choosing an image or verifying variant match.
 | 
				
			||||||
 | 
						VariantChoice string
 | 
				
			||||||
	// If not "", overrides the system's default directory containing a blob info cache.
 | 
						// If not "", overrides the system's default directory containing a blob info cache.
 | 
				
			||||||
	BlobInfoCacheDir string
 | 
						BlobInfoCacheDir string
 | 
				
			||||||
	// Additional tags when creating or copying a docker-archive.
 | 
						// Additional tags when creating or copying a docker-archive.
 | 
				
			||||||
| 
						 | 
					@ -540,7 +553,10 @@ type SystemContext struct {
 | 
				
			||||||
	// Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
 | 
						// Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
 | 
				
			||||||
	DockerInsecureSkipTLSVerify OptionalBool
 | 
						DockerInsecureSkipTLSVerify OptionalBool
 | 
				
			||||||
	// if nil, the library tries to parse ~/.docker/config.json to retrieve credentials
 | 
						// if nil, the library tries to parse ~/.docker/config.json to retrieve credentials
 | 
				
			||||||
 | 
						// Ignored if DockerBearerRegistryToken is non-empty.
 | 
				
			||||||
	DockerAuthConfig *DockerAuthConfig
 | 
						DockerAuthConfig *DockerAuthConfig
 | 
				
			||||||
 | 
						// if not "", the library uses this registry token to authenticate to the registry
 | 
				
			||||||
 | 
						DockerBearerRegistryToken string
 | 
				
			||||||
	// if not "", an User-Agent header is added to each request when contacting a registry.
 | 
						// if not "", an User-Agent header is added to each request when contacting a registry.
 | 
				
			||||||
	DockerRegistryUserAgent string
 | 
						DockerRegistryUserAgent string
 | 
				
			||||||
	// if true, a V1 ping attempt isn't done to give users a better error. Default is false.
 | 
						// if true, a V1 ping attempt isn't done to give users a better error. Default is false.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ const (
 | 
				
			||||||
	// VersionMajor is for an API incompatible changes
 | 
						// VersionMajor is for an API incompatible changes
 | 
				
			||||||
	VersionMajor = 5
 | 
						VersionMajor = 5
 | 
				
			||||||
	// VersionMinor is for functionality in a backwards-compatible manner
 | 
						// VersionMinor is for functionality in a backwards-compatible manner
 | 
				
			||||||
	VersionMinor = 2
 | 
						VersionMinor = 3
 | 
				
			||||||
	// VersionPatch is for backwards-compatible bug fixes
 | 
						// VersionPatch is for backwards-compatible bug fixes
 | 
				
			||||||
	VersionPatch = 1
 | 
						VersionPatch = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,28 +0,0 @@
 | 
				
			||||||
package bbolt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import "unsafe"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// maxMapSize represents the largest mmap size supported by Bolt.
 | 
					 | 
				
			||||||
const maxMapSize = 0x7FFFFFFF // 2GB
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// maxAllocSize is the size used when creating array pointers.
 | 
					 | 
				
			||||||
const maxAllocSize = 0xFFFFFFF
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Are unaligned load/stores broken on this arch?
 | 
					 | 
				
			||||||
var brokenUnaligned bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	// Simple check to see whether this arch handles unaligned load/stores
 | 
					 | 
				
			||||||
	// correctly.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ARM9 and older devices require load/stores to be from/to aligned
 | 
					 | 
				
			||||||
	// addresses. If not, the lower 2 bits are cleared and that address is
 | 
					 | 
				
			||||||
	// read in a jumbled up order.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.html
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	raw := [6]byte{0xfe, 0xef, 0x11, 0x22, 0x22, 0x11}
 | 
					 | 
				
			||||||
	val := *(*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&raw)) + 2))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	brokenUnaligned = val != 0x11222211
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
Copyright (c) 2014-2016  Ulrich Kunitz
 | 
					Copyright (c) 2014-2020  Ulrich Kunitz
 | 
				
			||||||
All rights reserved.
 | 
					All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Redistribution and use in source and binary forms, with or without
 | 
					Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,9 @@
 | 
				
			||||||
# TODO list
 | 
					# TODO list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Release v0.5.x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Support check flag in gxz command.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Release v0.6
 | 
					## Release v0.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
1. Review encoder and check for lzma improvements under xz.
 | 
					1. Review encoder and check for lzma improvements under xz.
 | 
				
			||||||
| 
						 | 
					@ -86,6 +90,11 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Log
 | 
					## Log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2020-02-24
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Release v0.5.7 supports the check-ID None and fixes
 | 
				
			||||||
 | 
					[issue #27](https://github.com/ulikunitz/xz/issues/27).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 2019-02-20
 | 
					### 2019-02-20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Release v0.5.6 supports the go.mod file.
 | 
					Release v0.5.6 supports the go.mod file.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,7 +46,8 @@ const HeaderLen = 12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Constants for the checksum methods supported by xz.
 | 
					// Constants for the checksum methods supported by xz.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	CRC32  byte = 0x1
 | 
						None   byte = 0x0
 | 
				
			||||||
 | 
						CRC32       = 0x1
 | 
				
			||||||
	CRC64       = 0x4
 | 
						CRC64       = 0x4
 | 
				
			||||||
	SHA256      = 0xa
 | 
						SHA256      = 0xa
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -58,7 +59,7 @@ var errInvalidFlags = errors.New("xz: invalid flags")
 | 
				
			||||||
// invalid.
 | 
					// invalid.
 | 
				
			||||||
func verifyFlags(flags byte) error {
 | 
					func verifyFlags(flags byte) error {
 | 
				
			||||||
	switch flags {
 | 
						switch flags {
 | 
				
			||||||
	case CRC32, CRC64, SHA256:
 | 
						case None, CRC32, CRC64, SHA256:
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return errInvalidFlags
 | 
							return errInvalidFlags
 | 
				
			||||||
| 
						 | 
					@ -67,6 +68,7 @@ func verifyFlags(flags byte) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// flagstrings maps flag values to strings.
 | 
					// flagstrings maps flag values to strings.
 | 
				
			||||||
var flagstrings = map[byte]string{
 | 
					var flagstrings = map[byte]string{
 | 
				
			||||||
 | 
						None:   "None",
 | 
				
			||||||
	CRC32:  "CRC-32",
 | 
						CRC32:  "CRC-32",
 | 
				
			||||||
	CRC64:  "CRC-64",
 | 
						CRC64:  "CRC-64",
 | 
				
			||||||
	SHA256: "SHA-256",
 | 
						SHA256: "SHA-256",
 | 
				
			||||||
| 
						 | 
					@ -85,6 +87,8 @@ func flagString(flags byte) string {
 | 
				
			||||||
// hash method encoded in flags.
 | 
					// hash method encoded in flags.
 | 
				
			||||||
func newHashFunc(flags byte) (newHash func() hash.Hash, err error) {
 | 
					func newHashFunc(flags byte) (newHash func() hash.Hash, err error) {
 | 
				
			||||||
	switch flags {
 | 
						switch flags {
 | 
				
			||||||
 | 
						case None:
 | 
				
			||||||
 | 
							newHash = newNoneHash
 | 
				
			||||||
	case CRC32:
 | 
						case CRC32:
 | 
				
			||||||
		newHash = newCRC32
 | 
							newHash = newCRC32
 | 
				
			||||||
	case CRC64:
 | 
						case CRC64:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -1 +1,3 @@
 | 
				
			||||||
module github.com/ulikunitz/xz
 | 
					module github.com/ulikunitz/xz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.12
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package xz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "hash"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type noneHash struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h noneHash) Write(p []byte) (n int, err error) { return len(p), nil }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h noneHash) Sum(b []byte) []byte { return b }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h noneHash) Reset() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h noneHash) Size() int { return 0 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h noneHash) BlockSize() int { return 0 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newNoneHash() hash.Hash {
 | 
				
			||||||
 | 
						return &noneHash{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -283,7 +283,11 @@ func (c *ReaderConfig) newBlockReader(xz io.Reader, h *blockHeader,
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if br.hash.Size() != 0 {
 | 
				
			||||||
		br.r = io.TeeReader(fr, br.hash)
 | 
							br.r = io.TeeReader(fr, br.hash)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							br.r = fr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return br, nil
 | 
						return br, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
 | 
					// Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,8 +18,10 @@ type WriterConfig struct {
 | 
				
			||||||
	DictCap    int
 | 
						DictCap    int
 | 
				
			||||||
	BufSize    int
 | 
						BufSize    int
 | 
				
			||||||
	BlockSize  int64
 | 
						BlockSize  int64
 | 
				
			||||||
	// checksum method: CRC32, CRC64 or SHA256
 | 
						// checksum method: CRC32, CRC64 or SHA256 (default: CRC64)
 | 
				
			||||||
	CheckSum byte
 | 
						CheckSum byte
 | 
				
			||||||
 | 
						// Forces NoChecksum (default: false)
 | 
				
			||||||
 | 
						NoCheckSum bool
 | 
				
			||||||
	// match algorithm
 | 
						// match algorithm
 | 
				
			||||||
	Matcher lzma.MatchAlgorithm
 | 
						Matcher lzma.MatchAlgorithm
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -41,6 +43,9 @@ func (c *WriterConfig) fill() {
 | 
				
			||||||
	if c.CheckSum == 0 {
 | 
						if c.CheckSum == 0 {
 | 
				
			||||||
		c.CheckSum = CRC64
 | 
							c.CheckSum = CRC64
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if c.NoCheckSum {
 | 
				
			||||||
 | 
							c.CheckSum = None
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Verify checks the configuration for errors. Zero values will be
 | 
					// Verify checks the configuration for errors. Zero values will be
 | 
				
			||||||
| 
						 | 
					@ -284,7 +289,11 @@ func (c *WriterConfig) newBlockWriter(xz io.Writer, hash hash.Hash) (bw *blockWr
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if bw.hash.Size() != 0 {
 | 
				
			||||||
		bw.mw = io.MultiWriter(bw.w, bw.hash)
 | 
							bw.mw = io.MultiWriter(bw.w, bw.hash)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							bw.mw = bw.w
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return bw, nil
 | 
						return bw, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,11 +29,11 @@ func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
 | 
				
			||||||
	f(w, width, stat)
 | 
						f(w, width, stat)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Wrapper interface.
 | 
					// WrapFiller interface.
 | 
				
			||||||
// If you're implementing custom Filler by wrapping a built-in one,
 | 
					// If you're implementing custom Filler by wrapping a built-in one,
 | 
				
			||||||
// it is necessary to implement this interface to retain functionality
 | 
					// it is necessary to implement this interface to retain functionality
 | 
				
			||||||
// of built-in Filler.
 | 
					// of built-in Filler.
 | 
				
			||||||
type Wrapper interface {
 | 
					type WrapFiller interface {
 | 
				
			||||||
	Base() Filler
 | 
						Base() Filler
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,13 +18,14 @@ const (
 | 
				
			||||||
	rRefill
 | 
						rRefill
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DefaultBarStyle is applied when bar constructed with *Progress.AddBar method.
 | 
					// DefaultBarStyle is a string containing 7 runes.
 | 
				
			||||||
 | 
					// Each rune is a building block of a progress bar.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	'1th rune' stands for left boundary rune
 | 
					//	'1st rune' stands for left boundary rune
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	'2th rune' stands for fill rune
 | 
					//	'2nd rune' stands for fill rune
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	'3th rune' stands for tip rune
 | 
					//	'3rd rune' stands for tip rune
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	'4th rune' stands for empty rune
 | 
					//	'4th rune' stands for empty rune
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
| 
						 | 
					@ -44,16 +45,16 @@ type barFiller struct {
 | 
				
			||||||
	flush   func(w io.Writer, bb [][]byte)
 | 
						flush   func(w io.Writer, bb [][]byte)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewBarFiller constucts mpb.Filler, to be used with *Progress.Add method.
 | 
					// NewBarFiller constucts mpb.Filler, to be used with *Progress.Add(...) *Bar method.
 | 
				
			||||||
func NewBarFiller(style string, reverse bool) Filler {
 | 
					func NewBarFiller(style string, reverse bool) Filler {
 | 
				
			||||||
	if style == "" {
 | 
						if style == "" {
 | 
				
			||||||
		style = DefaultBarStyle
 | 
							style = DefaultBarStyle
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	bf := &barFiller{
 | 
						bf := &barFiller{
 | 
				
			||||||
		format:  make([][]byte, utf8.RuneCountInString(style)),
 | 
							format:  make([][]byte, utf8.RuneCountInString(style)),
 | 
				
			||||||
 | 
							reverse: reverse,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	bf.SetStyle(style)
 | 
						bf.SetStyle(style)
 | 
				
			||||||
	bf.SetReverse(reverse)
 | 
					 | 
				
			||||||
	return bf
 | 
						return bf
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,28 +67,16 @@ func (s *barFiller) SetStyle(style string) {
 | 
				
			||||||
		src = append(src, []byte(string(r)))
 | 
							src = append(src, []byte(string(r)))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	copy(s.format, src)
 | 
						copy(s.format, src)
 | 
				
			||||||
	if s.reverse {
 | 
						s.SetReverse(s.reverse)
 | 
				
			||||||
		s.tip = s.format[rRevTip]
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		s.tip = s.format[rTip]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *barFiller) SetReverse(reverse bool) {
 | 
					func (s *barFiller) SetReverse(reverse bool) {
 | 
				
			||||||
	if reverse {
 | 
						if reverse {
 | 
				
			||||||
		s.tip = s.format[rRevTip]
 | 
							s.tip = s.format[rRevTip]
 | 
				
			||||||
		s.flush = func(w io.Writer, bb [][]byte) {
 | 
							s.flush = reverseFlush
 | 
				
			||||||
			for i := len(bb) - 1; i >= 0; i-- {
 | 
					 | 
				
			||||||
				w.Write(bb[i])
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		s.tip = s.format[rTip]
 | 
							s.tip = s.format[rTip]
 | 
				
			||||||
		s.flush = func(w io.Writer, bb [][]byte) {
 | 
							s.flush = normalFlush
 | 
				
			||||||
			for i := 0; i < len(bb); i++ {
 | 
					 | 
				
			||||||
				w.Write(bb[i])
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	s.reverse = reverse
 | 
						s.reverse = reverse
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -135,3 +124,15 @@ func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.flush(w, bb)
 | 
						s.flush(w, bb)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func normalFlush(w io.Writer, bb [][]byte) {
 | 
				
			||||||
 | 
						for i := 0; i < len(bb); i++ {
 | 
				
			||||||
 | 
							w.Write(bb[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func reverseFlush(w io.Writer, bb [][]byte) {
 | 
				
			||||||
 | 
						for i := len(bb) - 1; i >= 0; i-- {
 | 
				
			||||||
 | 
							w.Write(bb[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -199,8 +199,8 @@ func MakeFillerTypeSpecificBarOption(
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BarOptOnCond returns option when condition evaluates to true.
 | 
					// BarOptOn returns option when condition evaluates to true.
 | 
				
			||||||
func BarOptOnCond(option BarOption, condition func() bool) BarOption {
 | 
					func BarOptOn(option BarOption, condition func() bool) BarOption {
 | 
				
			||||||
	if condition() {
 | 
						if condition() {
 | 
				
			||||||
		return option
 | 
							return option
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					package decor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Any decorator displays text, that can be changed during decorator's
 | 
				
			||||||
 | 
					// lifetime via provided func call back.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	`f` call back which provides string to display
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	`wcc` optional WC config
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					func Any(f func(*Statistics) string, wcc ...WC) Decorator {
 | 
				
			||||||
 | 
						return &any{initWC(wcc...), f}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type any struct {
 | 
				
			||||||
 | 
						WC
 | 
				
			||||||
 | 
						f func(*Statistics) string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *any) Decor(s *Statistics) string {
 | 
				
			||||||
 | 
						return d.FormatMsg(d.f(s))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -43,24 +43,7 @@ func CountersKiloByte(pairFmt string, wcc ...WC) Decorator {
 | 
				
			||||||
//	pairFmt="% d / % d"     output: "1 MB / 12 MB"
 | 
					//	pairFmt="% d / % d"     output: "1 MB / 12 MB"
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func Counters(unit int, pairFmt string, wcc ...WC) Decorator {
 | 
					func Counters(unit int, pairFmt string, wcc ...WC) Decorator {
 | 
				
			||||||
	var wc WC
 | 
						return Any(chooseSizeProducer(unit, pairFmt), wcc...)
 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
					 | 
				
			||||||
		wc = widthConf
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	d := &countersDecorator{
 | 
					 | 
				
			||||||
		WC:       wc.Init(),
 | 
					 | 
				
			||||||
		producer: chooseSizeProducer(unit, pairFmt),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return d
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type countersDecorator struct {
 | 
					 | 
				
			||||||
	WC
 | 
					 | 
				
			||||||
	producer func(*Statistics) string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *countersDecorator) Decor(st *Statistics) string {
 | 
					 | 
				
			||||||
	return d.FormatMsg(d.producer(st))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func chooseSizeProducer(unit int, format string) func(*Statistics) string {
 | 
					func chooseSizeProducer(unit int, format string) func(*Statistics) string {
 | 
				
			||||||
| 
						 | 
					@ -69,16 +52,16 @@ func chooseSizeProducer(unit int, format string) func(*Statistics) string {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	switch unit {
 | 
						switch unit {
 | 
				
			||||||
	case UnitKiB:
 | 
						case UnitKiB:
 | 
				
			||||||
		return func(st *Statistics) string {
 | 
							return func(s *Statistics) string {
 | 
				
			||||||
			return fmt.Sprintf(format, SizeB1024(st.Current), SizeB1024(st.Total))
 | 
								return fmt.Sprintf(format, SizeB1024(s.Current), SizeB1024(s.Total))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case UnitKB:
 | 
						case UnitKB:
 | 
				
			||||||
		return func(st *Statistics) string {
 | 
							return func(s *Statistics) string {
 | 
				
			||||||
			return fmt.Sprintf(format, SizeB1000(st.Current), SizeB1000(st.Total))
 | 
								return fmt.Sprintf(format, SizeB1000(s.Current), SizeB1000(s.Total))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return func(st *Statistics) string {
 | 
							return func(s *Statistics) string {
 | 
				
			||||||
			return fmt.Sprintf(format, st.Current, st.Total)
 | 
								return fmt.Sprintf(format, s.Current, s.Total)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -176,3 +176,11 @@ func (wc *WC) GetConf() WC {
 | 
				
			||||||
func (wc *WC) SetConf(conf WC) {
 | 
					func (wc *WC) SetConf(conf WC) {
 | 
				
			||||||
	*wc = conf.Init()
 | 
						*wc = conf.Init()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func initWC(wcc ...WC) WC {
 | 
				
			||||||
 | 
						var wc WC
 | 
				
			||||||
 | 
						for _, nwc := range wcc {
 | 
				
			||||||
 | 
							wc = nwc
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return wc.Init()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ import (
 | 
				
			||||||
//	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | 
					//	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`wcc` optional WC config
 | 
					//	`wcc` optional WC config
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
func Elapsed(style TimeStyle, wcc ...WC) Decorator {
 | 
					func Elapsed(style TimeStyle, wcc ...WC) Decorator {
 | 
				
			||||||
	return NewElapsed(style, time.Now(), wcc...)
 | 
						return NewElapsed(style, time.Now(), wcc...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -20,29 +21,15 @@ func Elapsed(style TimeStyle, wcc ...WC) Decorator {
 | 
				
			||||||
//	`startTime` start time
 | 
					//	`startTime` start time
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`wcc` optional WC config
 | 
					//	`wcc` optional WC config
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
 | 
					func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
 | 
				
			||||||
	var wc WC
 | 
						var msg string
 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
						producer := chooseTimeProducer(style)
 | 
				
			||||||
		wc = widthConf
 | 
						f := func(s *Statistics) string {
 | 
				
			||||||
 | 
							if !s.Completed {
 | 
				
			||||||
 | 
								msg = producer(time.Since(startTime))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	d := &elapsedDecorator{
 | 
							return msg
 | 
				
			||||||
		WC:        wc.Init(),
 | 
					 | 
				
			||||||
		startTime: startTime,
 | 
					 | 
				
			||||||
		producer:  chooseTimeProducer(style),
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return d
 | 
						return Any(f, wcc...)
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type elapsedDecorator struct {
 | 
					 | 
				
			||||||
	WC
 | 
					 | 
				
			||||||
	startTime time.Time
 | 
					 | 
				
			||||||
	producer  func(time.Duration) string
 | 
					 | 
				
			||||||
	msg       string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *elapsedDecorator) Decor(st *Statistics) string {
 | 
					 | 
				
			||||||
	if !st.Completed {
 | 
					 | 
				
			||||||
		d.msg = d.producer(time.Since(d.startTime))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return d.FormatMsg(d.msg)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,7 @@ func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		average = ewma.NewMovingAverage(age)
 | 
							average = ewma.NewMovingAverage(age)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return MovingAverageETA(style, average, nil, wcc...)
 | 
						return MovingAverageETA(style, NewThreadSafeMovingAverage(average), nil, wcc...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MovingAverageETA decorator relies on MovingAverage implementation to calculate its average.
 | 
					// MovingAverageETA decorator relies on MovingAverage implementation to calculate its average.
 | 
				
			||||||
| 
						 | 
					@ -45,13 +45,10 @@ func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
 | 
				
			||||||
//	`normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
 | 
					//	`normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`wcc` optional WC config
 | 
					//	`wcc` optional WC config
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
func MovingAverageETA(style TimeStyle, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
 | 
					func MovingAverageETA(style TimeStyle, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
 | 
				
			||||||
	var wc WC
 | 
					 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
					 | 
				
			||||||
		wc = widthConf
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	d := &movingAverageETA{
 | 
						d := &movingAverageETA{
 | 
				
			||||||
		WC:         wc.Init(),
 | 
							WC:         initWC(wcc...),
 | 
				
			||||||
		average:    average,
 | 
							average:    average,
 | 
				
			||||||
		normalizer: normalizer,
 | 
							normalizer: normalizer,
 | 
				
			||||||
		producer:   chooseTimeProducer(style),
 | 
							producer:   chooseTimeProducer(style),
 | 
				
			||||||
| 
						 | 
					@ -66,9 +63,9 @@ type movingAverageETA struct {
 | 
				
			||||||
	producer   func(time.Duration) string
 | 
						producer   func(time.Duration) string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *movingAverageETA) Decor(st *Statistics) string {
 | 
					func (d *movingAverageETA) Decor(s *Statistics) string {
 | 
				
			||||||
	v := math.Round(d.average.Value())
 | 
						v := math.Round(d.average.Value())
 | 
				
			||||||
	remaining := time.Duration((st.Total - st.Current) * int64(v))
 | 
						remaining := time.Duration((s.Total - s.Current) * int64(v))
 | 
				
			||||||
	if d.normalizer != nil {
 | 
						if d.normalizer != nil {
 | 
				
			||||||
		remaining = d.normalizer.Normalize(remaining)
 | 
							remaining = d.normalizer.Normalize(remaining)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -92,6 +89,7 @@ func (d *movingAverageETA) NextAmount(n int64, wdd ...time.Duration) {
 | 
				
			||||||
//	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | 
					//	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`wcc` optional WC config
 | 
					//	`wcc` optional WC config
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
func AverageETA(style TimeStyle, wcc ...WC) Decorator {
 | 
					func AverageETA(style TimeStyle, wcc ...WC) Decorator {
 | 
				
			||||||
	return NewAverageETA(style, time.Now(), nil, wcc...)
 | 
						return NewAverageETA(style, time.Now(), nil, wcc...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -105,13 +103,10 @@ func AverageETA(style TimeStyle, wcc ...WC) Decorator {
 | 
				
			||||||
//	`normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
 | 
					//	`normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`wcc` optional WC config
 | 
					//	`wcc` optional WC config
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator {
 | 
					func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator {
 | 
				
			||||||
	var wc WC
 | 
					 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
					 | 
				
			||||||
		wc = widthConf
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	d := &averageETA{
 | 
						d := &averageETA{
 | 
				
			||||||
		WC:         wc.Init(),
 | 
							WC:         initWC(wcc...),
 | 
				
			||||||
		startTime:  startTime,
 | 
							startTime:  startTime,
 | 
				
			||||||
		normalizer: normalizer,
 | 
							normalizer: normalizer,
 | 
				
			||||||
		producer:   chooseTimeProducer(style),
 | 
							producer:   chooseTimeProducer(style),
 | 
				
			||||||
| 
						 | 
					@ -126,12 +121,12 @@ type averageETA struct {
 | 
				
			||||||
	producer   func(time.Duration) string
 | 
						producer   func(time.Duration) string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *averageETA) Decor(st *Statistics) string {
 | 
					func (d *averageETA) Decor(s *Statistics) string {
 | 
				
			||||||
	var remaining time.Duration
 | 
						var remaining time.Duration
 | 
				
			||||||
	if st.Current != 0 {
 | 
						if s.Current != 0 {
 | 
				
			||||||
		durPerItem := float64(time.Since(d.startTime)) / float64(st.Current)
 | 
							durPerItem := float64(time.Since(d.startTime)) / float64(s.Current)
 | 
				
			||||||
		durPerItem = math.Round(durPerItem)
 | 
							durPerItem = math.Round(durPerItem)
 | 
				
			||||||
		remaining = time.Duration((st.Total - st.Current) * int64(durPerItem))
 | 
							remaining = time.Duration((s.Total - s.Current) * int64(durPerItem))
 | 
				
			||||||
		if d.normalizer != nil {
 | 
							if d.normalizer != nil {
 | 
				
			||||||
			remaining = d.normalizer.Normalize(remaining)
 | 
								remaining = d.normalizer.Normalize(remaining)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,8 +64,8 @@ func (d *mergeDecorator) Base() Decorator {
 | 
				
			||||||
	return d.Decorator
 | 
						return d.Decorator
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *mergeDecorator) Decor(st *Statistics) string {
 | 
					func (d *mergeDecorator) Decor(s *Statistics) string {
 | 
				
			||||||
	msg := d.Decorator.Decor(st)
 | 
						msg := d.Decorator.Decor(s)
 | 
				
			||||||
	msgLen := utf8.RuneCountInString(msg)
 | 
						msgLen := utf8.RuneCountInString(msg)
 | 
				
			||||||
	if (d.wc.C & DextraSpace) != 0 {
 | 
						if (d.wc.C & DextraSpace) != 0 {
 | 
				
			||||||
		msgLen++
 | 
							msgLen++
 | 
				
			||||||
| 
						 | 
					@ -101,6 +101,6 @@ type placeHolderDecorator struct {
 | 
				
			||||||
	WC
 | 
						WC
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *placeHolderDecorator) Decor(_ *Statistics) string {
 | 
					func (d *placeHolderDecorator) Decor(*Statistics) string {
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ package decor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/VividCortex/ewma"
 | 
						"github.com/VividCortex/ewma"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -11,6 +12,38 @@ import (
 | 
				
			||||||
// or exponentially decaying.
 | 
					// or exponentially decaying.
 | 
				
			||||||
type MovingAverage = ewma.MovingAverage
 | 
					type MovingAverage = ewma.MovingAverage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type threadSafeMovingAverage struct {
 | 
				
			||||||
 | 
						ewma.MovingAverage
 | 
				
			||||||
 | 
						mu sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *threadSafeMovingAverage) Add(value float64) {
 | 
				
			||||||
 | 
						s.mu.Lock()
 | 
				
			||||||
 | 
						s.MovingAverage.Add(value)
 | 
				
			||||||
 | 
						s.mu.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *threadSafeMovingAverage) Value() float64 {
 | 
				
			||||||
 | 
						s.mu.Lock()
 | 
				
			||||||
 | 
						defer s.mu.Unlock()
 | 
				
			||||||
 | 
						return s.MovingAverage.Value()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *threadSafeMovingAverage) Set(value float64) {
 | 
				
			||||||
 | 
						s.mu.Lock()
 | 
				
			||||||
 | 
						s.MovingAverage.Set(value)
 | 
				
			||||||
 | 
						s.mu.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewThreadSafeMovingAverage converts provided ewma.MovingAverage
 | 
				
			||||||
 | 
					// into thread safe ewma.MovingAverage.
 | 
				
			||||||
 | 
					func NewThreadSafeMovingAverage(average ewma.MovingAverage) ewma.MovingAverage {
 | 
				
			||||||
 | 
						if tsma, ok := average.(*threadSafeMovingAverage); ok {
 | 
				
			||||||
 | 
							return tsma
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &threadSafeMovingAverage{MovingAverage: average}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type medianWindow [3]float64
 | 
					type medianWindow [3]float64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *medianWindow) Len() int           { return len(s) }
 | 
					func (s *medianWindow) Len() int           { return len(s) }
 | 
				
			||||||
| 
						 | 
					@ -36,5 +69,5 @@ func (s *medianWindow) Set(value float64) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewMedian is fixed last 3 samples median MovingAverage.
 | 
					// NewMedian is fixed last 3 samples median MovingAverage.
 | 
				
			||||||
func NewMedian() MovingAverage {
 | 
					func NewMedian() MovingAverage {
 | 
				
			||||||
	return new(medianWindow)
 | 
						return NewThreadSafeMovingAverage(new(medianWindow))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,27 +1,12 @@
 | 
				
			||||||
package decor
 | 
					package decor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Name returns name decorator.
 | 
					// Name decorator displays text that is set once and can't be changed
 | 
				
			||||||
 | 
					// during decorator's lifetime.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`name` string to display
 | 
					//	`str` string to display
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`wcc` optional WC config
 | 
					//	`wcc` optional WC config
 | 
				
			||||||
func Name(name string, wcc ...WC) Decorator {
 | 
					//
 | 
				
			||||||
	var wc WC
 | 
					func Name(str string, wcc ...WC) Decorator {
 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
						return Any(func(*Statistics) string { return str }, wcc...)
 | 
				
			||||||
		wc = widthConf
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	d := &nameDecorator{
 | 
					 | 
				
			||||||
		WC:  wc.Init(),
 | 
					 | 
				
			||||||
		msg: name,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return d
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type nameDecorator struct {
 | 
					 | 
				
			||||||
	WC
 | 
					 | 
				
			||||||
	msg string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *nameDecorator) Decor(st *Statistics) string {
 | 
					 | 
				
			||||||
	return d.FormatMsg(d.msg)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package decor
 | 
				
			||||||
//	`decorator` Decorator to wrap
 | 
					//	`decorator` Decorator to wrap
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`message` message to display on complete event
 | 
					//	`message` message to display on complete event
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
func OnComplete(decorator Decorator, message string) Decorator {
 | 
					func OnComplete(decorator Decorator, message string) Decorator {
 | 
				
			||||||
	d := &onCompleteWrapper{
 | 
						d := &onCompleteWrapper{
 | 
				
			||||||
		Decorator: decorator,
 | 
							Decorator: decorator,
 | 
				
			||||||
| 
						 | 
					@ -23,12 +24,12 @@ type onCompleteWrapper struct {
 | 
				
			||||||
	msg string
 | 
						msg string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *onCompleteWrapper) Decor(st *Statistics) string {
 | 
					func (d *onCompleteWrapper) Decor(s *Statistics) string {
 | 
				
			||||||
	if st.Completed {
 | 
						if s.Completed {
 | 
				
			||||||
		wc := d.GetConf()
 | 
							wc := d.GetConf()
 | 
				
			||||||
		return wc.FormatMsg(d.msg)
 | 
							return wc.FormatMsg(d.msg)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return d.Decorator.Decor(st)
 | 
						return d.Decorator.Decor(s)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *onCompleteWrapper) Base() Decorator {
 | 
					func (d *onCompleteWrapper) Base() Decorator {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,36 +37,22 @@ func Percentage(wcc ...WC) Decorator {
 | 
				
			||||||
	return NewPercentage("% d", wcc...)
 | 
						return NewPercentage("% d", wcc...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewPercentage percentage decorator with custom fmt string.
 | 
					// NewPercentage percentage decorator with custom format string.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// fmt examples:
 | 
					// format examples:
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	fmt="%.1f"  output: "1.0%"
 | 
					//	format="%.1f"  output: "1.0%"
 | 
				
			||||||
//	fmt="% .1f" output: "1.0 %"
 | 
					//	format="% .1f" output: "1.0 %"
 | 
				
			||||||
//	fmt="%d"    output: "1%"
 | 
					//	format="%d"    output: "1%"
 | 
				
			||||||
//	fmt="% d"   output: "1 %"
 | 
					//	format="% d"   output: "1 %"
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func NewPercentage(fmt string, wcc ...WC) Decorator {
 | 
					func NewPercentage(format string, wcc ...WC) Decorator {
 | 
				
			||||||
	var wc WC
 | 
						if format == "" {
 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
							format = "% d"
 | 
				
			||||||
		wc = widthConf
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if fmt == "" {
 | 
						f := func(s *Statistics) string {
 | 
				
			||||||
		fmt = "% d"
 | 
							p := internal.Percentage(s.Total, s.Current, 100)
 | 
				
			||||||
 | 
							return fmt.Sprintf(format, percentageType(p))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	d := &percentageDecorator{
 | 
						return Any(f, wcc...)
 | 
				
			||||||
		WC:  wc.Init(),
 | 
					 | 
				
			||||||
		fmt: fmt,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return d
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type percentageDecorator struct {
 | 
					 | 
				
			||||||
	WC
 | 
					 | 
				
			||||||
	fmt string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *percentageDecorator) Decor(st *Statistics) string {
 | 
					 | 
				
			||||||
	p := internal.Percentage(st.Total, st.Current, 100)
 | 
					 | 
				
			||||||
	return d.FormatMsg(fmt.Sprintf(d.fmt, percentageType(p)))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,12 +9,20 @@ import (
 | 
				
			||||||
	"github.com/VividCortex/ewma"
 | 
						"github.com/VividCortex/ewma"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SpeedFormatter is wrapper for SizeB1024 and SizeB1000 to format value as speed/s.
 | 
					// FmtAsSpeed adds "/s" to the end of the input formatter. To be
 | 
				
			||||||
type SpeedFormatter struct {
 | 
					// used with SizeB1000 or SizeB1024 types, for example:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048)))
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					func FmtAsSpeed(input fmt.Formatter) fmt.Formatter {
 | 
				
			||||||
 | 
						return &speedFormatter{input}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type speedFormatter struct {
 | 
				
			||||||
	fmt.Formatter
 | 
						fmt.Formatter
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (self *SpeedFormatter) Format(st fmt.State, verb rune) {
 | 
					func (self *speedFormatter) Format(st fmt.State, verb rune) {
 | 
				
			||||||
	self.Formatter.Format(st, verb)
 | 
						self.Formatter.Format(st, verb)
 | 
				
			||||||
	io.WriteString(st, "/s")
 | 
						io.WriteString(st, "/s")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -30,7 +38,7 @@ func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		average = ewma.NewMovingAverage(age)
 | 
							average = ewma.NewMovingAverage(age)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return MovingAverageSpeed(unit, format, average, wcc...)
 | 
						return MovingAverageSpeed(unit, format, NewThreadSafeMovingAverage(average), wcc...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MovingAverageSpeed decorator relies on MovingAverage implementation
 | 
					// MovingAverageSpeed decorator relies on MovingAverage implementation
 | 
				
			||||||
| 
						 | 
					@ -52,15 +60,11 @@ func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
 | 
				
			||||||
//	unit=UnitKB,  format="% .1f" output: "1.0 MB/s"
 | 
					//	unit=UnitKB,  format="% .1f" output: "1.0 MB/s"
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func MovingAverageSpeed(unit int, format string, average MovingAverage, wcc ...WC) Decorator {
 | 
					func MovingAverageSpeed(unit int, format string, average MovingAverage, wcc ...WC) Decorator {
 | 
				
			||||||
	var wc WC
 | 
					 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
					 | 
				
			||||||
		wc = widthConf
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if format == "" {
 | 
						if format == "" {
 | 
				
			||||||
		format = "%.0f"
 | 
							format = "%.0f"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	d := &movingAverageSpeed{
 | 
						d := &movingAverageSpeed{
 | 
				
			||||||
		WC:       wc.Init(),
 | 
							WC:       initWC(wcc...),
 | 
				
			||||||
		average:  average,
 | 
							average:  average,
 | 
				
			||||||
		producer: chooseSpeedProducer(unit, format),
 | 
							producer: chooseSpeedProducer(unit, format),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -74,8 +78,8 @@ type movingAverageSpeed struct {
 | 
				
			||||||
	msg      string
 | 
						msg      string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *movingAverageSpeed) Decor(st *Statistics) string {
 | 
					func (d *movingAverageSpeed) Decor(s *Statistics) string {
 | 
				
			||||||
	if !st.Completed {
 | 
						if !s.Completed {
 | 
				
			||||||
		var speed float64
 | 
							var speed float64
 | 
				
			||||||
		if v := d.average.Value(); v > 0 {
 | 
							if v := d.average.Value(); v > 0 {
 | 
				
			||||||
			speed = 1 / v
 | 
								speed = 1 / v
 | 
				
			||||||
| 
						 | 
					@ -122,15 +126,11 @@ func AverageSpeed(unit int, format string, wcc ...WC) Decorator {
 | 
				
			||||||
//	unit=UnitKB,  format="% .1f" output: "1.0 MB/s"
 | 
					//	unit=UnitKB,  format="% .1f" output: "1.0 MB/s"
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator {
 | 
					func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator {
 | 
				
			||||||
	var wc WC
 | 
					 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
					 | 
				
			||||||
		wc = widthConf
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if format == "" {
 | 
						if format == "" {
 | 
				
			||||||
		format = "%.0f"
 | 
							format = "%.0f"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	d := &averageSpeed{
 | 
						d := &averageSpeed{
 | 
				
			||||||
		WC:        wc.Init(),
 | 
							WC:        initWC(wcc...),
 | 
				
			||||||
		startTime: startTime,
 | 
							startTime: startTime,
 | 
				
			||||||
		producer:  chooseSpeedProducer(unit, format),
 | 
							producer:  chooseSpeedProducer(unit, format),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -144,9 +144,9 @@ type averageSpeed struct {
 | 
				
			||||||
	msg       string
 | 
						msg       string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *averageSpeed) Decor(st *Statistics) string {
 | 
					func (d *averageSpeed) Decor(s *Statistics) string {
 | 
				
			||||||
	if !st.Completed {
 | 
						if !s.Completed {
 | 
				
			||||||
		speed := float64(st.Current) / float64(time.Since(d.startTime))
 | 
							speed := float64(s.Current) / float64(time.Since(d.startTime))
 | 
				
			||||||
		d.msg = d.producer(speed * 1e9)
 | 
							d.msg = d.producer(speed * 1e9)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,11 +161,11 @@ func chooseSpeedProducer(unit int, format string) func(float64) string {
 | 
				
			||||||
	switch unit {
 | 
						switch unit {
 | 
				
			||||||
	case UnitKiB:
 | 
						case UnitKiB:
 | 
				
			||||||
		return func(speed float64) string {
 | 
							return func(speed float64) string {
 | 
				
			||||||
			return fmt.Sprintf(format, &SpeedFormatter{SizeB1024(math.Round(speed))})
 | 
								return fmt.Sprintf(format, FmtAsSpeed(SizeB1024(math.Round(speed))))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case UnitKB:
 | 
						case UnitKB:
 | 
				
			||||||
		return func(speed float64) string {
 | 
							return func(speed float64) string {
 | 
				
			||||||
			return fmt.Sprintf(format, &SpeedFormatter{SizeB1000(math.Round(speed))})
 | 
								return fmt.Sprintf(format, FmtAsSpeed(SizeB1000(math.Round(speed))))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return func(speed float64) string {
 | 
							return func(speed float64) string {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,28 +8,14 @@ var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//	`wcc` optional WC config
 | 
					//	`wcc` optional WC config
 | 
				
			||||||
func Spinner(frames []string, wcc ...WC) Decorator {
 | 
					func Spinner(frames []string, wcc ...WC) Decorator {
 | 
				
			||||||
	var wc WC
 | 
					 | 
				
			||||||
	for _, widthConf := range wcc {
 | 
					 | 
				
			||||||
		wc = widthConf
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(frames) == 0 {
 | 
						if len(frames) == 0 {
 | 
				
			||||||
		frames = defaultSpinnerStyle
 | 
							frames = defaultSpinnerStyle
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	d := &spinnerDecorator{
 | 
						var count uint
 | 
				
			||||||
		WC:     wc.Init(),
 | 
						f := func(s *Statistics) string {
 | 
				
			||||||
		frames: frames,
 | 
							frame := frames[count%uint(len(frames))]
 | 
				
			||||||
 | 
							count++
 | 
				
			||||||
 | 
							return frame
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return d
 | 
						return Any(f, wcc...)
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type spinnerDecorator struct {
 | 
					 | 
				
			||||||
	WC
 | 
					 | 
				
			||||||
	frames []string
 | 
					 | 
				
			||||||
	count  uint
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *spinnerDecorator) Decor(st *Statistics) string {
 | 
					 | 
				
			||||||
	frame := d.frames[d.count%uint(len(d.frames))]
 | 
					 | 
				
			||||||
	d.count++
 | 
					 | 
				
			||||||
	return d.FormatMsg(frame)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,8 @@ module github.com/vbauerster/mpb/v4
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/VividCortex/ewma v1.1.1
 | 
						github.com/VividCortex/ewma v1.1.1
 | 
				
			||||||
	github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
 | 
						github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708
 | 
						golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
 | 
				
			||||||
	golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 // indirect
 | 
						golang.org/x/sys v0.0.0-20200217220822-9197077df867 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
go 1.13
 | 
					go 1.13
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,11 +3,11 @@ github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmx
 | 
				
			||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
 | 
					github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
 | 
				
			||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
 | 
					github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
 | 
					golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 h1:dHtDnRWQtSx0Hjq9kvKFpBh9uPPKfQN70NZZmvssGwk=
 | 
					golang.org/x/sys v0.0.0-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,8 +96,8 @@ func PopCompletedMode() ContainerOption {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ContainerOptOnCond returns option when condition evaluates to true.
 | 
					// ContainerOptOn returns option when condition evaluates to true.
 | 
				
			||||||
func ContainerOptOnCond(option ContainerOption, condition func() bool) ContainerOption {
 | 
					func ContainerOptOn(option ContainerOption, condition func() bool) ContainerOption {
 | 
				
			||||||
	if condition() {
 | 
						if condition() {
 | 
				
			||||||
		return option
 | 
							return option
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"container/heap"
 | 
						"container/heap"
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
| 
						 | 
					@ -97,18 +98,19 @@ func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddBar creates a new progress bar and adds to the container.
 | 
					// AddBar creates a new progress bar and adds it to the rendering queue.
 | 
				
			||||||
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
 | 
					func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
 | 
				
			||||||
	return p.Add(total, NewBarFiller(DefaultBarStyle, false), options...)
 | 
						return p.Add(total, NewBarFiller(DefaultBarStyle, false), options...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddSpinner creates a new spinner bar and adds to the container.
 | 
					// AddSpinner creates a new spinner bar and adds it to the rendering queue.
 | 
				
			||||||
func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
 | 
					func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
 | 
				
			||||||
	return p.Add(total, NewSpinnerFiller(DefaultSpinnerStyle, alignment), options...)
 | 
						return p.Add(total, NewSpinnerFiller(DefaultSpinnerStyle, alignment), options...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Add creates a bar which renders itself by provided filler.
 | 
					// Add creates a bar which renders itself by provided filler.
 | 
				
			||||||
// Set total to 0, if you plan to update it later.
 | 
					// Set total to 0, if you plan to update it later.
 | 
				
			||||||
 | 
					// Panics if *Progress instance is done, i.e. called after *Progress.Wait().
 | 
				
			||||||
func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar {
 | 
					func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar {
 | 
				
			||||||
	if filler == nil {
 | 
						if filler == nil {
 | 
				
			||||||
		filler = NewBarFiller(DefaultBarStyle, false)
 | 
							filler = NewBarFiller(DefaultBarStyle, false)
 | 
				
			||||||
| 
						 | 
					@ -134,7 +136,7 @@ func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar {
 | 
				
			||||||
		return bar
 | 
							return bar
 | 
				
			||||||
	case <-p.done:
 | 
						case <-p.done:
 | 
				
			||||||
		p.bwg.Done()
 | 
							p.bwg.Done()
 | 
				
			||||||
		return nil
 | 
							panic(fmt.Sprintf("%T instance can't be reused after it's done!", p))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -387,7 +389,7 @@ func syncWidth(matrix map[int][]chan int) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func extractBaseFiller(f Filler) Filler {
 | 
					func extractBaseFiller(f Filler) Filler {
 | 
				
			||||||
	if f, ok := f.(Wrapper); ok {
 | 
						if f, ok := f.(WrapFiller); ok {
 | 
				
			||||||
		return extractBaseFiller(f.Base())
 | 
							return extractBaseFiller(f.Base())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return f
 | 
						return f
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ const (
 | 
				
			||||||
	SpinnerOnRight
 | 
						SpinnerOnRight
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DefaultSpinnerStyle is applied when bar constructed with *Progress.AddSpinner method.
 | 
					// DefaultSpinnerStyle is a slice of strings, which makes a spinner.
 | 
				
			||||||
var DefaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
 | 
					var DefaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type spinnerFiller struct {
 | 
					type spinnerFiller struct {
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ type spinnerFiller struct {
 | 
				
			||||||
	alignment SpinnerAlignment
 | 
						alignment SpinnerAlignment
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewSpinnerFiller constucts mpb.Filler, to be used with *Progress.Add method.
 | 
					// NewSpinnerFiller constucts mpb.Filler, to be used with *Progress.Add(...) *Bar method.
 | 
				
			||||||
func NewSpinnerFiller(style []string, alignment SpinnerAlignment) Filler {
 | 
					func NewSpinnerFiller(style []string, alignment SpinnerAlignment) Filler {
 | 
				
			||||||
	if len(style) == 0 {
 | 
						if len(style) == 0 {
 | 
				
			||||||
		style = DefaultSpinnerStyle
 | 
							style = DefaultSpinnerStyle
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										0
									
								
								vendor/github.com/etcd-io/bbolt/.gitignore → vendor/go.etcd.io/bbolt/.gitignore
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										0
									
								
								vendor/github.com/etcd-io/bbolt/.gitignore → vendor/go.etcd.io/bbolt/.gitignore
								
								
									generated
								
								
									vendored
								
								
							
							
								
								
									
										2
									
								
								vendor/github.com/etcd-io/bbolt/.travis.yml → vendor/go.etcd.io/bbolt/.travis.yml
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										2
									
								
								vendor/github.com/etcd-io/bbolt/.travis.yml → vendor/go.etcd.io/bbolt/.travis.yml
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
					@ -4,7 +4,7 @@ go_import_path: go.etcd.io/bbolt
 | 
				
			||||||
sudo: false
 | 
					sudo: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
go:
 | 
					go:
 | 
				
			||||||
- 1.11
 | 
					- 1.12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
before_install:
 | 
					before_install:
 | 
				
			||||||
- go get -v honnef.co/go/tools/...
 | 
					- go get -v honnef.co/go/tools/...
 | 
				
			||||||
| 
						 | 
					@ -275,7 +275,7 @@ should be writable.
 | 
				
			||||||
### Using buckets
 | 
					### Using buckets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Buckets are collections of key/value pairs within the database. All keys in a
 | 
					Buckets are collections of key/value pairs within the database. All keys in a
 | 
				
			||||||
bucket must be unique. You can create a bucket using the `DB.CreateBucket()`
 | 
					bucket must be unique. You can create a bucket using the `Tx.CreateBucket()`
 | 
				
			||||||
function:
 | 
					function:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```go
 | 
					```go
 | 
				
			||||||
| 
						 | 
					@ -923,6 +923,7 @@ Below is a list of public, open source projects that use Bolt:
 | 
				
			||||||
* [GoWebApp](https://github.com/josephspurrier/gowebapp) - A basic MVC web application in Go using BoltDB.
 | 
					* [GoWebApp](https://github.com/josephspurrier/gowebapp) - A basic MVC web application in Go using BoltDB.
 | 
				
			||||||
* [GoShort](https://github.com/pankajkhairnar/goShort) - GoShort is a URL shortener written in Golang and BoltDB for persistent key/value storage and for routing it's using high performent HTTPRouter.
 | 
					* [GoShort](https://github.com/pankajkhairnar/goShort) - GoShort is a URL shortener written in Golang and BoltDB for persistent key/value storage and for routing it's using high performent HTTPRouter.
 | 
				
			||||||
* [gopherpit](https://github.com/gopherpit/gopherpit) - A web service to manage Go remote import paths with custom domains
 | 
					* [gopherpit](https://github.com/gopherpit/gopherpit) - A web service to manage Go remote import paths with custom domains
 | 
				
			||||||
 | 
					* [gokv](https://github.com/philippgille/gokv) - Simple key-value store abstraction and implementations for Go (Redis, Consul, etcd, bbolt, BadgerDB, LevelDB, Memcached, DynamoDB, S3, PostgreSQL, MongoDB, CockroachDB and many more)
 | 
				
			||||||
* [Gitchain](https://github.com/gitchain/gitchain) - Decentralized, peer-to-peer Git repositories aka "Git meets Bitcoin".
 | 
					* [Gitchain](https://github.com/gitchain/gitchain) - Decentralized, peer-to-peer Git repositories aka "Git meets Bitcoin".
 | 
				
			||||||
* [InfluxDB](https://influxdata.com) - Scalable datastore for metrics, events, and real-time analytics.
 | 
					* [InfluxDB](https://influxdata.com) - Scalable datastore for metrics, events, and real-time analytics.
 | 
				
			||||||
* [ipLocator](https://github.com/AndreasBriese/ipLocator) - A fast ip-geo-location-server using bolt with bloom filters.
 | 
					* [ipLocator](https://github.com/AndreasBriese/ipLocator) - A fast ip-geo-location-server using bolt with bloom filters.
 | 
				
			||||||
| 
						 | 
					@ -935,6 +936,7 @@ Below is a list of public, open source projects that use Bolt:
 | 
				
			||||||
* [mbuckets](https://github.com/abhigupta912/mbuckets) - A Bolt wrapper that allows easy operations on multi level (nested) buckets.
 | 
					* [mbuckets](https://github.com/abhigupta912/mbuckets) - A Bolt wrapper that allows easy operations on multi level (nested) buckets.
 | 
				
			||||||
* [MetricBase](https://github.com/msiebuhr/MetricBase) - Single-binary version of Graphite.
 | 
					* [MetricBase](https://github.com/msiebuhr/MetricBase) - Single-binary version of Graphite.
 | 
				
			||||||
* [MuLiFS](https://github.com/dankomiocevic/mulifs) - Music Library Filesystem creates a filesystem to organise your music files.
 | 
					* [MuLiFS](https://github.com/dankomiocevic/mulifs) - Music Library Filesystem creates a filesystem to organise your music files.
 | 
				
			||||||
 | 
					* [NATS](https://github.com/nats-io/nats-streaming-server) - NATS Streaming uses bbolt for message and metadata storage.
 | 
				
			||||||
* [Operation Go: A Routine Mission](http://gocode.io) - An online programming game for Golang using Bolt for user accounts and a leaderboard.
 | 
					* [Operation Go: A Routine Mission](http://gocode.io) - An online programming game for Golang using Bolt for user accounts and a leaderboard.
 | 
				
			||||||
* [photosite/session](https://godoc.org/bitbucket.org/kardianos/photosite/session) - Sessions for a photo viewing site.
 | 
					* [photosite/session](https://godoc.org/bitbucket.org/kardianos/photosite/session) - Sessions for a photo viewing site.
 | 
				
			||||||
* [Prometheus Annotation Server](https://github.com/oliver006/prom_annotation_server) - Annotation server for PromDash & Prometheus service monitoring system.
 | 
					* [Prometheus Annotation Server](https://github.com/oliver006/prom_annotation_server) - Annotation server for PromDash & Prometheus service monitoring system.
 | 
				
			||||||
							
								
								
									
										3
									
								
								vendor/github.com/etcd-io/bbolt/bolt_386.go → vendor/go.etcd.io/bbolt/bolt_386.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										3
									
								
								vendor/github.com/etcd-io/bbolt/bolt_386.go → vendor/go.etcd.io/bbolt/bolt_386.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
					@ -5,6 +5,3 @@ const maxMapSize = 0x7FFFFFFF // 2GB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// maxAllocSize is the size used when creating array pointers.
 | 
					// maxAllocSize is the size used when creating array pointers.
 | 
				
			||||||
const maxAllocSize = 0xFFFFFFF
 | 
					const maxAllocSize = 0xFFFFFFF
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Are unaligned load/stores broken on this arch?
 | 
					 | 
				
			||||||
var brokenUnaligned = false
 | 
					 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue