Repository: osbuild/osbuild-composer
Pull request #2734 (2022-06-09 15:38:24)

dnfjson: add repository name and URL to repo-related error messages
If dnf-json returns an error that is related to a repository, it uses the ID to identify the repository that caused the error.  Since IDs can't easily be mapped back to a configuration, appending the URL and name (if any) to the error message makes it easier to identify which repository failed.

Keeping the ID in the message is also useful for finding the cache directory of the repository if needed.

For reference, this is what an error message looks like when it has both a name and URL:
```
DNF error occurred: RepoError: There was a problem reading a repository: Failed to download metadata for repo '7a71592ff8a03486179a467c6416bf29e6a7ce46ffee9bc8aa703ff07c3b16ad' [baseos: https://0.0.0.0/baseos/repo]: Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried
```

achilleas-k: Rebased and resolved conflicts.
achilleas-k: > Looks good.
> 
> However, DNF uses repo `id` only if the repo `name` is not set. We could actually omit the logic of modifying the error added in this PR and instead ensure that we always set the repo `name` to a reasonable value. Basically setting the repo `name` to the same human-readable value `[name: URL]` here
> 
> https://github.com/osbuild/osbuild-composer/blob/09f57b6c2f8199a0a8a9365dfb56b3e52e7a60e4/internal/dnfjson/dnfjson.go#L186

I tried this.  The error always gets reported with the repo ID.
achilleas-k: A minimal example:
```
$ cat dnfname.py
import dnf


base = dnf.Base()
base.conf.persistdir = "/tmp/rpmpersist"
base.conf.cachedir = "/tmp/rpmcache"


repo = dnf.repo.Repo("repoid", base.conf)
repo.name = "reponame"
repo.baseurl = "https://0.0.0.0/repo"

base.repos.add(repo)
base.fill_sack(load_system_repo=False)
base.install_specs(["whatever"], reponame="repoid")
base.resolve()

$ python3 ./dnfname.py
Errors during downloading metadata for repository 'repoid':
  - Curl error (7): Couldn't connect to server for https://0.0.0.0/repo/repodata/repomd.xml [Failed to connect to 0.0.0.0 port 443: Connection refused]
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/dnf/repo.py", line 573, in load
    ret = self._repo.load()
  File "/usr/lib64/python3.9/site-packages/libdnf/repo.py", line 331, in load
    return _repo.Repo_load(self)
libdnf._error.Error: Failed to download metadata for repo 'repoid': Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/achilleas/./dnfname.py", line 14, in <module>
    base.fill_sack(load_system_repo=False)
  File "/usr/lib/python3.9/site-packages/dnf/base.py", line 399, in fill_sack
    self._add_repo_to_sack(r)
  File "/usr/lib/python3.9/site-packages/dnf/base.py", line 139, in _add_repo_to_sack
    repo.load()
  File "/usr/lib/python3.9/site-packages/dnf/repo.py", line 580, in load
    raise dnf.exceptions.RepoError(str(e))
dnf.exceptions.RepoError: Failed to download metadata for repo 'repoid': Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried
```

The URL appears in the first exception here because it's coming from `Curl` failing to connect to the server.  In most of our cases there's no curl error so it doesn't appear.
The `repo.name` (`reponame`) doesn't show up in the error even though I set it.  This even happens if I call `install_specs()` with `reponame` instead of `repoid`.
achilleas-k: And now fedora builds are failing because it doesn't use the vendored version of `testify` so it doesn't have the (I guess new) `assert.ErrorContains()` function.

Patch:
diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml
index e239e2603..d985a4d94 100644
--- a/.github/workflows/create-tag.yml
+++ b/.github/workflows/create-tag.yml
@@ -4,7 +4,7 @@ name: "Create and push release tag"
 on:
   workflow_dispatch:
   schedule:
-    - cron: "0 13 * * 3"
+    - cron: "0 8 * * 3"
 
 jobs:
   tag-and-push:
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2351b82f3..d823e52d0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,7 +14,6 @@ stages:
     - schutzbot/ci_details.sh > ci-details-after-run || true
     - schutzbot/update_github_status.sh update || true
     - schutzbot/save_journal.sh || true
-    - schutzbot/run_cloud_cleaner.sh || true
   interruptible: true
   tags:
     - terraform
diff --git a/cmd/osbuild-upload-azure/main.go b/cmd/osbuild-upload-azure/main.go
index 7a260241f..2c25adf62 100644
--- a/cmd/osbuild-upload-azure/main.go
+++ b/cmd/osbuild-upload-azure/main.go
@@ -1,10 +1,12 @@
 package main
 
 import (
+	"context"
 	"flag"
 	"fmt"
 	"os"
 	"path"
+	"strings"
 
 	"github.com/osbuild/osbuild-composer/internal/upload/azure"
 )
@@ -17,17 +19,37 @@ func checkStringNotEmpty(variable string, errorMessage string) {
 	}
 }
 
+type tags map[string]string
+
+func (t *tags) String() string {
+	return ""
+}
+
+func (t *tags) Set(value string) error {
+	splitValue := strings.SplitN(value, ":", 2)
+	if len(splitValue) < 2 {
+		return fmt.Errorf(`-tag must be in format key:value, "%s" is not valid`, value)
+	}
+	key := splitValue[0]
+	val := splitValue[1]
+	(*t)[key] = val
+
+	return nil
+}
+
 func main() {
 	var storageAccount string
 	var storageAccessKey string
 	var fileName string
 	var containerName string
 	var threads int
+	tagsArg := tags(make(map[string]string))
 	flag.StringVar(&storageAccount, "storage-account", "", "Azure storage account (mandatory)")
 	flag.StringVar(&storageAccessKey, "storage-access-key", "", "Azure storage access key (mandatory)")
 	flag.StringVar(&fileName, "image", "", "image to upload (mandatory)")
 	flag.StringVar(&containerName, "container", "", "name of storage container (see Azure docs for explanation, mandatory)")
 	flag.IntVar(&threads, "threads", 16, "number of threads for parallel upload")
+	flag.Var(&tagsArg, "tag", "blob tag formatted as key:value (first colon found is considered to be the delimiter), can be specified multiple times")
 	flag.Parse()
 
 	checkStringNotEmpty(storageAccount, "You need to specify storage account")
@@ -43,17 +65,25 @@ func main() {
 		os.Exit(1)
 	}
 
+	blobName := azure.EnsureVHDExtension(path.Base(fileName))
+	blobMetadata := azure.BlobMetadata{
+		StorageAccount: storageAccount,
+		BlobName:       blobName,
+		ContainerName:  containerName,
+	}
 	err = c.UploadPageBlob(
-		azure.BlobMetadata{
-			StorageAccount: storageAccount,
-			BlobName:       path.Base(fileName),
-			ContainerName:  containerName,
-		},
+		blobMetadata,
 		fileName,
 		threads,
 	)
+	if err != nil {
+		fmt.Println("Uploading error: ", err)
+		os.Exit(1)
+	}
 
+	err = c.TagBlob(context.Background(), blobMetadata, tagsArg)
 	if err != nil {
-		fmt.Println("Error: ", err)
+		fmt.Println("Tagging error: ", err)
+		os.Exit(1)
 	}
 }
diff --git a/cmd/osbuild-worker/jobimpl-osbuild.go b/cmd/osbuild-worker/jobimpl-osbuild.go
index 00d5529e3..101165fc6 100644
--- a/cmd/osbuild-worker/jobimpl-osbuild.go
+++ b/cmd/osbuild-worker/jobimpl-osbuild.go
@@ -527,10 +527,12 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				return err
 			}
 
+			// Azure cannot create an image from a blob without .vhd extension
+			blobName := azure.EnsureVHDExtension(args.Targets[0].ImageName)
 			metadata := azure.BlobMetadata{
 				StorageAccount: options.StorageAccount,
 				ContainerName:  options.Container,
-				BlobName:       args.Targets[0].ImageName,
+				BlobName:       blobName,
 			}
 
 			const azureMaxUploadGoroutines = 4
@@ -677,10 +679,8 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				return nil
 			}
 
-			blobName := args.Targets[0].ImageName
-			if !strings.HasSuffix(blobName, ".vhd") {
-				blobName += ".vhd"
-			}
+			// Azure cannot create an image from a blob without .vhd extension
+			blobName := azure.EnsureVHDExtension(args.Targets[0].ImageName)
 
 			logWithId.Info("[Azure] ⬆ Uploading the image")
 			err = azureStorageClient.UploadPageBlob(
diff --git a/go.mod b/go.mod
index 751f8a216..32b3714c8 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	cloud.google.com/go/storage v1.22.0
 	github.com/Azure/azure-pipeline-go v0.2.3
 	github.com/Azure/azure-sdk-for-go v63.4.0+incompatible
-	github.com/Azure/azure-storage-blob-go v0.14.0
+	github.com/Azure/azure-storage-blob-go v0.13.0
 	github.com/Azure/go-autorest/autorest v0.11.27
 	github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
 	github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
@@ -47,5 +47,6 @@ require (
 	golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
 	google.golang.org/api v0.75.0
 	google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3
+	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 	gopkg.in/ini.v1 v1.66.4
 )
diff --git a/go.sum b/go.sum
index 5272cdf29..c5a17fae8 100644
--- a/go.sum
+++ b/go.sum
@@ -62,14 +62,14 @@ github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVt
 github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
 github.com/Azure/azure-sdk-for-go v63.4.0+incompatible h1:fle3M5Q7vr8auaiPffKyUQmLbvYeqpw30bKU6PrWJFo=
 github.com/Azure/azure-sdk-for-go v63.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM=
-github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
+github.com/Azure/azure-storage-blob-go v0.13.0 h1:lgWHvFh+UYBNVQLFHXkvul2f6yOPA9PIH82RTG2cSwc=
+github.com/Azure/azure-storage-blob-go v0.13.0/go.mod h1:pA9kNqtjUeQF2zOSu4s//nUdBD+e64lEuc4sVnuOfNs=
 github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
 github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
 github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A=
 github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
-github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
+github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
 github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
 github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
 github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA=
@@ -162,7 +162,6 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
 github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
-github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
@@ -276,8 +275,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
 github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -579,7 +578,6 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
diff --git a/internal/dnfjson/dnfjson.go b/internal/dnfjson/dnfjson.go
index 5d29a4319..2aecf3695 100644
--- a/internal/dnfjson/dnfjson.go
+++ b/internal/dnfjson/dnfjson.go
@@ -21,6 +21,7 @@ import (
 	"os"
 	"os/exec"
 	"sort"
+	"strings"
 	"time"
 
 	"github.com/osbuild/osbuild-composer/internal/rhsm"
@@ -407,8 +408,10 @@ func (err Error) Error() string {
 	return fmt.Sprintf("DNF error occurred: %s: %s", err.Kind, err.Reason)
 }
 
-// parseError parses the response from dnf-json into the Error type.
-func parseError(data []byte) Error {
+// parseError parses the response from dnf-json into the Error type and appends
+// the name and URL of a repository to all detected repository IDs in the
+// message.
+func parseError(data []byte, repos []repoConfig) Error {
 	var e Error
 	if err := json.Unmarshal(data, &e); err != nil {
 		// dumping the error into the Reason can get noisy, but it's good for troubleshooting
@@ -417,6 +420,25 @@ func parseError(data []byte) Error {
 			Reason: fmt.Sprintf("Failed to unmarshal dnf-json error output %q: %s", string(data), err.Error()),
 		}
 	}
+
+	// append to any instance of a repository ID the URL (or metalink, mirrorlist, etc)
+	for _, repo := range repos {
+		idstr := fmt.Sprintf("'%s'", repo.ID)
+		var nameURL string
+		if len(repo.BaseURL) > 0 {
+			nameURL = repo.BaseURL
+		} else if len(repo.Metalink) > 0 {
+			nameURL = repo.Metalink
+		} else if len(repo.MirrorList) > 0 {
+			nameURL = repo.MirrorList
+		}
+
+		if len(repo.Name) > 0 {
+			nameURL = fmt.Sprintf("%s: %s", repo.Name, nameURL)
+		}
+		e.Reason = strings.Replace(e.Reason, idstr, fmt.Sprintf("%s [%s]", idstr, nameURL), -1)
+	}
+
 	return e
 }
 
@@ -453,7 +475,7 @@ func run(dnfJsonCmd []string, req *Request) ([]byte, error) {
 	err = cmd.Wait()
 	output := stdout.Bytes()
 	if runError, ok := err.(*exec.ExitError); ok && runError.ExitCode() != 0 {
-		return nil, parseError(output)
+		return nil, parseError(output, req.Arguments.Repos)
 	}
 
 	return output, nil
diff --git a/internal/osbuild1/stage.go b/internal/osbuild1/stage.go
index 624ae992a..32b0fd120 100644
--- a/internal/osbuild1/stage.go
+++ b/internal/osbuild1/stage.go
@@ -1,10 +1,5 @@
 package osbuild1
 
-import (
-	"encoding/json"
-	"fmt"
-)
-
 // A Stage transforms a filesystem tree.
 type Stage struct {
 	// Well-known name in reverse domain-name notation, uniquely identifying
@@ -19,71 +14,3 @@ type Stage struct {
 type StageOptions interface {
 	isStageOptions()
 }
-
-type rawStage struct {
-	Name    string          `json:"name"`
-	Options json.RawMessage `json:"options"`
-}
-
-// UnmarshalJSON unmarshals JSON into a Stage object. Each type of stage has
-// a custom unmarshaller for its options, selected based on the stage name.
-func (stage *Stage) UnmarshalJSON(data []byte) error {
-	var rawStage rawStage
-	err := json.Unmarshal(data, &rawStage)
-	if err != nil {
-		return err
-	}
-	var options StageOptions
-	switch rawStage.Name {
-	case "org.osbuild.fix-bls":
-		// TODO: verify that we can unmarshall this also if "options" is omitted
-		options = new(FixBLSStageOptions)
-	case "org.osbuild.fstab":
-		options = new(FSTabStageOptions)
-	case "org.osbuild.grub2":
-		options = new(GRUB2StageOptions)
-	case "org.osbuild.locale":
-		options = new(LocaleStageOptions)
-	case "org.osbuild.selinux":
-		options = new(SELinuxStageOptions)
-	case "org.osbuild.hostname":
-		options = new(HostnameStageOptions)
-	case "org.osbuild.users":
-		options = new(UsersStageOptions)
-	case "org.osbuild.groups":
-		options = new(GroupsStageOptions)
-	case "org.osbuild.timezone":
-		options = new(TimezoneStageOptions)
-	case "org.osbuild.chrony":
-		options = new(ChronyStageOptions)
-	case "org.osbuild.keymap":
-		options = new(KeymapStageOptions)
-	case "org.osbuild.firewall":
-		options = new(FirewallStageOptions)
-	case "org.osbuild.rhsm":
-		options = new(RHSMStageOptions)
-	case "org.osbuild.rpm":
-		options = new(RPMStageOptions)
-	case "org.osbuild.rpm-ostree":
-		options = new(RPMOSTreeStageOptions)
-	case "org.osbuild.systemd":
-		options = new(SystemdStageOptions)
-	case "org.osbuild.script":
-		options = new(ScriptStageOptions)
-	case "org.osbuild.sysconfig":
-		options = new(SysconfigStageOptions)
-	case "org.osbuild.kernel-cmdline":
-		options = new(KernelCmdlineStageOptions)
-	default:
-		return fmt.Errorf("unexpected stage name: %s", rawStage.Name)
-	}
-	err = json.Unmarshal(rawStage.Options, options)
-	if err != nil {
-		return err
-	}
-
-	stage.Name = rawStage.Name
-	stage.Options = options
-
-	return nil
-}
diff --git a/internal/osbuild2/stage.go b/internal/osbuild2/stage.go
index 73630c7e4..639c56f4b 100644
--- a/internal/osbuild2/stage.go
+++ b/internal/osbuild2/stage.go
@@ -1,10 +1,5 @@
 package osbuild2
 
-import (
-	"encoding/json"
-	"fmt"
-)
-
 // Single stage of a pipeline executing one step
 type Stage struct {
 	// Well-known name in reverse domain-name notation, uniquely identifying
@@ -22,165 +17,3 @@ type Stage struct {
 type StageOptions interface {
 	isStageOptions()
 }
-
-type rawStage struct {
-	Type    string          `json:"type"`
-	Options json.RawMessage `json:"options"`
-	Inputs  json.RawMessage `json:"inputs"`
-	Devices json.RawMessage `json:"devices"`
-	Mounts  json.RawMessage `json:"mounts"`
-}
-
-// UnmarshalJSON unmarshals JSON into a Stage object. Each type of stage has
-// a custom unmarshaller for its options, selected based on the stage name.
-func (stage *Stage) UnmarshalJSON(data []byte) error {
-	var rawStage rawStage
-	if err := json.Unmarshal(data, &rawStage); err != nil {
-		return err
-	}
-	var options StageOptions
-	var inputs Inputs
-	var devices Devices
-	var mounts Mounts
-	switch rawStage.Type {
-	case "org.osbuild.authselect":
-		options = new(AuthselectStageOptions)
-	case "org.osbuild.fix-bls":
-		options = new(FixBLSStageOptions)
-	case "org.osbuild.fstab":
-		options = new(FSTabStageOptions)
-	case "org.osbuild.grub2":
-		options = new(GRUB2StageOptions)
-	case "org.osbuild.locale":
-		options = new(LocaleStageOptions)
-	case "org.osbuild.selinux":
-		options = new(SELinuxStageOptions)
-	case "org.osbuild.selinux.config":
-		options = new(SELinuxConfigStageOptions)
-	case "org.osbuild.hostname":
-		options = new(HostnameStageOptions)
-	case "org.osbuild.users":
-		options = new(UsersStageOptions)
-	case "org.osbuild.groups":
-		options = new(GroupsStageOptions)
-	case "org.osbuild.timezone":
-		options = new(TimezoneStageOptions)
-	case "org.osbuild.cloud-init":
-		options = new(CloudInitStageOptions)
-	case "org.osbuild.chrony":
-		options = new(ChronyStageOptions)
-	case "org.osbuild.dnf.config":
-		options = new(DNFConfigStageOptions)
-	case "org.osbuild.dnf-automatic.config":
-		options = new(DNFAutomaticConfigStageOptions)
-	case "org.osbuild.dracut":
-		options = new(DracutStageOptions)
-	case "org.osbuild.dracut.conf":
-		options = new(DracutConfStageOptions)
-	case "org.osbuild.keymap":
-		options = new(KeymapStageOptions)
-	case "org.osbuild.modprobe":
-		options = new(ModprobeStageOptions)
-	case "org.osbuild.firewall":
-		options = new(FirewallStageOptions)
-	case "org.osbuild.rhsm":
-		options = new(RHSMStageOptions)
-	case "org.osbuild.systemd":
-		options = new(SystemdStageOptions)
-	case "org.osbuild.systemd.unit":
-		options = new(SystemdUnitStageOptions)
-	case "org.osbuild.systemd-logind":
-		options = new(SystemdLogindStageOptions)
-	case "org.osbuild.script":
-		options = new(ScriptStageOptions)
-	case "org.osbuild.sysconfig":
-		options = new(SysconfigStageOptions)
-	case "org.osbuild.sysctld":
-		options = new(SysctldStageOptions)
-	case "org.osbuild.tmpfilesd":
-		options = new(TmpfilesdStageOptions)
-	case "org.osbuild.kernel-cmdline":
-		options = new(KernelCmdlineStageOptions)
-	case "org.osbuild.rpm":
-		options = new(RPMStageOptions)
-		inputs = new(RPMStageInputs)
-	case "org.osbuild.oci-archive":
-		options = new(OCIArchiveStageOptions)
-		inputs = new(OCIArchiveStageInputs)
-	case "org.osbuild.ostree.commit":
-		options = new(OSTreeCommitStageOptions)
-		inputs = new(OSTreeCommitStageInputs)
-	case "org.osbuild.ostree.passwd":
-		options = new(OSTreePasswdStageOptions)
-		inputs = new(OSTreePasswdStageInputs)
-	case "org.osbuild.ostree.pull":
-		options = new(OSTreePullStageOptions)
-		inputs = new(OSTreePullStageInputs)
-	case "org.osbuild.ostree.init":
-		options = new(OSTreeInitStageOptions)
-	case "org.osbuild.ostree.preptree":
-		options = new(OSTreePrepTreeStageOptions)
-	case "org.osbuild.pam.limits.conf":
-		options = new(PamLimitsConfStageOptions)
-	case "org.osbuild.truncate":
-		options = new(TruncateStageOptions)
-	case "org.osbuild.tuned":
-		options = new(TunedStageOptions)
-	case "org.osbuild.sfdisk":
-		options = new(SfdiskStageOptions)
-	case "org.osbuild.copy":
-		options = new(CopyStageOptions)
-		inputs = new(CopyStageInputs)
-	case "org.osbuild.mkfs.btrfs":
-		options = new(MkfsBtrfsStageOptions)
-	case "org.osbuild.mkfs.ext4":
-		options = new(MkfsExt4StageOptions)
-	case "org.osbuild.mkfs.fat":
-		options = new(MkfsFATStageOptions)
-	case "org.osbuild.mkfs.xfs":
-		options = new(MkfsXfsStageOptions)
-	case "org.osbuild.qemu":
-		options = new(QEMUStageOptions)
-		inputs = new(QEMUStageInputs)
-	case "org.osbuild.xz":
-		options = new(XzStageOptions)
-		// TODO: Unmarshalling inputs should be moved to a separate method and struct should be determined by its Type
-		// The stage accepts also source input, but we need to rework all inputs first to handle this nicely here.
-		// Only files input is used by the XZ stage at this moment.
-		inputs = new(FilesInputs)
-	case "org.osbuild.sshd.config":
-		options = new(SshdConfigStageOptions)
-	case "org.osbuild.authconfig":
-		options = new(AuthconfigStageOptions)
-	case "org.osbuild.pwquality.conf":
-		options = new(PwqualityConfStageOptions)
-	case "org.osbuild.yum.config":
-		options = new(YumConfigStageOptions)
-	case "org.osbuild.yum.repos":
-		options = new(YumReposStageOptions)
-	case "org.osbuild.luks2.format":
-		options = new(LUKS2CreateStageOptions)
-	case "org.osbuild.clevis.luks-bind":
-		options = new(ClevisLuksBindStageOptions)
-	case "org.osbuild.luks2.remove-key":
-		options = new(LUKS2RemoveKeyStageOptions)
-	default:
-		return fmt.Errorf("unexpected stage type: %s", rawStage.Type)
-	}
-	if err := json.Unmarshal(rawStage.Options, options); err != nil {
-		return err
-	}
-	if inputs != nil && rawStage.Inputs != nil {
-		if err := json.Unmarshal(rawStage.Inputs, inputs); err != nil {
-			return err
-		}
-	}
-
-	stage.Type = rawStage.Type
-	stage.Options = options
-	stage.Inputs = inputs
-	stage.Devices = devices
-	stage.Mounts = mounts
-
-	return nil
-}
diff --git a/internal/upload/azure/azurestorage.go b/internal/upload/azure/azurestorage.go
index d9821aef5..28bab380d 100644
--- a/internal/upload/azure/azurestorage.go
+++ b/internal/upload/azure/azurestorage.go
@@ -12,6 +12,7 @@ import (
 	"io"
 	"net/url"
 	"os"
+	"regexp"
 	"strings"
 	"sync"
 
@@ -56,12 +57,10 @@ const DefaultUploadThreads = 16
 // UploadPageBlob takes the metadata and credentials required to upload the image specified by `fileName`
 // It can speed up the upload by using goroutines. The number of parallel goroutines is bounded by
 // the `threads` argument.
+//
+// Note that if you want to create an image out of the page blob, make sure that metadata.BlobName
+// has a .vhd extension, see EnsureVHDExtension.
 func (c StorageClient) UploadPageBlob(metadata BlobMetadata, fileName string, threads int) error {
-	// Azure cannot create an image from a storage blob without .vhd extension
-	if !strings.HasSuffix(metadata.BlobName, ".vhd") {
-		metadata.BlobName = metadata.BlobName + ".vhd"
-	}
-
 	// get storage account blob service URL endpoint.
 	URL, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", metadata.StorageAccount, metadata.ContainerName))
 
@@ -196,6 +195,33 @@ func (c StorageClient) CreateStorageContainerIfNotExist(ctx context.Context, sto
 	return nil
 }
 
+// Taken from https://docs.microsoft.com/en-us/rest/api/storageservices/set-blob-tags#request-body
+var tagKeyRegexp = regexp.MustCompile(`^[a-zA-Z0-9 +-./:=_]{1,256}$`)
+var tagValueRegexp = regexp.MustCompile(`^[a-zA-Z0-9 +-./:=_]{0,256}$`)
+
+func (c StorageClient) TagBlob(ctx context.Context, metadata BlobMetadata, tags map[string]string) error {
+	for key, value := range tags {
+		if !tagKeyRegexp.MatchString(key) {
+			return fmt.Errorf("tag key `%s` doesn't match the format accepted by Azure", key)
+		}
+		if !tagValueRegexp.MatchString(key) {
+			return fmt.Errorf("tag value `%s` of key `%s` doesn't match the format accepted by Azure", value, key)
+		}
+	}
+
+	URL, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", metadata.StorageAccount, metadata.ContainerName))
+	containerURL := azblob.NewContainerURL(*URL, c.pipeline)
+
+	blobURL := containerURL.NewPageBlobURL(metadata.BlobName)
+
+	_, err := blobURL.SetTags(ctx, nil, nil, nil, nil, nil, nil, tags)
+	if err != nil {
+		return fmt.Errorf("cannot tag the blob: %v", err)
+	}
+
+	return nil
+}
+
 // RandomStorageAccountName returns a randomly generated name that can be used
 // for a storage account. This means that it must use only alphanumeric
 // characters and its length must be 24 or lower.
@@ -205,3 +231,13 @@ func RandomStorageAccountName(prefix string) string {
 
 	return (prefix + id)[:24]
 }
+
+// EnsureVHDExtension returns the given string with .vhd suffix if it already
+// doesn't have one.
+func EnsureVHDExtension(s string) string {
+	if strings.HasSuffix(s, ".vhd") {
+		return s
+	}
+
+	return s + ".vhd"
+}
diff --git a/schutzbot/run_cloud_cleaner.sh b/schutzbot/run_cloud_cleaner.sh
deleted file mode 100755
index f57a8b293..000000000
--- a/schutzbot/run_cloud_cleaner.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-
-source tools/set-env-variables.sh
-BRANCH_NAME="${BRANCH_NAME:-${CI_COMMIT_BRANCH}}"
-BUILD_ID="${BUILD_ID:-${CI_BUILD_ID}}"
-
-CLEANER_CMD="env $(cat "${AZURE_CREDS:-/dev/null}") BRANCH_NAME=$BRANCH_NAME BUILD_ID=$BUILD_ID DISTRO_CODE=$DISTRO_CODE /usr/libexec/osbuild-composer-test/cloud-cleaner"
-
-echo "🧹 Running the cloud cleaner"
-$CLEANER_CMD
diff --git a/schutzbot/scheduled_cloud_cleaner.sh b/schutzbot/scheduled_cloud_cleaner.sh
index abb92fac8..075e89f47 100755
--- a/schutzbot/scheduled_cloud_cleaner.sh
+++ b/schutzbot/scheduled_cloud_cleaner.sh
@@ -1,309 +1,5 @@
 #!/bin/bash
+git clone https://github.com/osbuild/cloud-cleaner.git
 
-# Colorful output.
-function greenprint {
-    echo -e "\033[1;32m[$(date -Isecond)] ${1}\033[0m"
-}
 
-#---------------------------------------------------------------
-#                       Azure cleanup
-#---------------------------------------------------------------
-
-greenprint "Starting azure cleanup"
-
-if ! hash az; then
-    # this installation method is taken from the official docs:
-    # https://docs.microsoft.com/cs-cz/cli/azure/install-azure-cli-linux?pivots=dnf
-    sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
-    echo -e "[azure-cli]
-name=Azure CLI
-baseurl=https://packages.microsoft.com/yumrepos/azure-cli
-enabled=1
-gpgcheck=1
-gpgkey=https://packages.microsoft.com/keys/microsoft.asc" | sudo tee /etc/yum.repos.d/azure-cli.repo
-
-  greenprint "Installing azure-cli"
-  sudo dnf install -y azure-cli
-  az version
-fi
-
-az login --service-principal --username "${V2_AZURE_CLIENT_ID}" --password "${V2_AZURE_CLIENT_SECRET}" --tenant "${AZURE_TENANT_ID}"
-
-# List all resources from AZURE_RESOURCE_GROUP
-RESOURCE_LIST=$(az resource list -g "$AZURE_RESOURCE_GROUP")
-RESOURCE_COUNT=$( echo "$RESOURCE_LIST" | jq .[].name | wc -l)
-
-# filter out resources older than X hours
-HOURS_BACK="${HOURS_BACK:-6}"
-DELETE_TIME=$(date -d "- $HOURS_BACK hours" +%s)
-OLD_RESOURCE_LIST_NAMES=()
-for i in $(seq 0 $(("$RESOURCE_COUNT"-1))); do
-    RESOURCE_TIME=$(echo "$RESOURCE_LIST" | jq ".[$i].createdTime" | tr -d '"')
-    RESOURCE_TYPE=$(echo "$RESOURCE_LIST" | jq ".[$i].type" | tr -d '"')
-    RESOURCE_TIME_SECONDS=$(date -d "$RESOURCE_TIME" +%s)
-    if [[ "$RESOURCE_TIME_SECONDS" -lt "$DELETE_TIME" && "$RESOURCE_TYPE" != Microsoft.Storage/storageAccounts ]]; then
-        OLD_RESOURCE_LIST_NAMES+=("$(echo "$RESOURCE_LIST" | jq .["$i"].name | sed -e 's/^[^-]*-//' | tr -d '"')")
-    fi
-done
-
-#Exit early if no there are no resources to delete
-if [ ${#OLD_RESOURCE_LIST_NAMES[@]} == 0 ]; then
-    echo "Nothing to delete in the standard storage account."
-fi
-
-# Keep only unique resource names
-mapfile -t RESOURCE_TO_DELETE_LIST  < <(printf "%s\n" "${OLD_RESOURCE_LIST_NAMES[@]}" | sort -u)
-echo "${RESOURCE_TO_DELETE_LIST[@]}"
-
-TO_DELETE_COUNT=${#RESOURCE_TO_DELETE_LIST[@]}
-echo "There are resources from $TO_DELETE_COUNT test runs to delete."
-
-for i in $(seq 0 $(("$TO_DELETE_COUNT"-1))); do
-    echo "Running cloud-cleaner in Azure for resources with TEST_ID: ${RESOURCE_TO_DELETE_LIST[$i]}"
-    TEST_ID=${RESOURCE_TO_DELETE_LIST[$i]} /usr/libexec/osbuild-composer-test/cloud-cleaner
-done
-
-# Explicitly check the other storage accounts (mostly the api test one)
-STORAGE_ACCOUNT_LIST=$(az resource list -g "$AZURE_RESOURCE_GROUP" --resource-type Microsoft.Storage/storageAccounts)
-STORAGE_ACCOUNT_COUNT=$(echo "$STORAGE_ACCOUNT_LIST" | jq .[].name | wc -l)
-DELETE_TIME=$(date -d "- $HOURS_BACK hours" +%s)
-for i in $(seq 0 $(("$STORAGE_ACCOUNT_COUNT"-1))); do
-    STORAGE_ACCOUNT_NAME=$(echo "$STORAGE_ACCOUNT_LIST" | jq .["$i"].name | tr -d '"')
-    echo "Checking storage account $STORAGE_ACCOUNT_NAME for old blobs."
-    
-    CONTAINER_LIST=$(az storage container list --account-name "$STORAGE_ACCOUNT_NAME")
-    CONTAINER_COUNT=$(echo "$CONTAINER_LIST" | jq .[].name | wc -l)
-    for i2 in $(seq 0 $(("$CONTAINER_COUNT"-1))); do
-        CONTAINER_NAME=$(echo "$CONTAINER_LIST" | jq .["$i2"].name | tr -d '"')
-        BLOB_LIST=$(az storage blob list --account-name "$STORAGE_ACCOUNT_NAME" --container-name "$CONTAINER_NAME")
-        BLOB_COUNT=$(echo "$BLOB_LIST" | jq .[].name | wc -l)
-        for i3 in $(seq 0 $(("$BLOB_COUNT"-1))); do
-            BLOB_NAME=$(echo "$BLOB_LIST" | jq .["$i3"].name | tr -d '"')
-            BLOB_TIME=$(echo "$BLOB_LIST" | jq .["$i3"].properties.lastModified | tr -d '"')
-            BLOB_TIME_SECONDS=$(date -d "$BLOB_TIME" +%s)
-            if [[ "$BLOB_TIME_SECONDS" -lt "$DELETE_TIME" ]]; then
-                echo "Deleting blob $BLOB_NAME in $STORAGE_ACCOUNT_NAME's $CONTAINER_NAME container."
-                az storage blob delete --only-show-errors --account-name "$STORAGE_ACCOUNT_NAME" --container-name "$CONTAINER_NAME" -n "$BLOB_NAME"
-            fi
-        done
-    done
-done
-
-echo "Azure cleanup complete!"
-
-
-#---------------------------------------------------------------
-# 			AWS cleanup
-#---------------------------------------------------------------
-
-greenprint "Starting aws cleanup"
-
-TEMPDIR=$(mktemp -d)
-function cleanup() {
-    sudo rm -rf "$TEMPDIR"
-}
-trap cleanup EXIT
-
-# Check available container runtime
-if which podman 2>/dev/null >&2; then
-    CONTAINER_RUNTIME=podman
-elif which docker 2>/dev/null >&2; then
-    CONTAINER_RUNTIME=docker
-else
-    echo No container runtime found, install podman or docker.
-    exit 2
-fi
-
-CONTAINER_IMAGE_CLOUD_TOOLS="quay.io/osbuild/cloud-tools:latest"
-
-SSH_DATA_DIR=$(/usr/libexec/osbuild-composer-test/gen-ssh.sh)
-
-# We need awscli to talk to AWS.
-if ! hash aws; then
-    echo "Using 'awscli' from a container"
-    sudo ${CONTAINER_RUNTIME} pull ${CONTAINER_IMAGE_CLOUD_TOOLS}
-
-    AWS_CMD="sudo ${CONTAINER_RUNTIME} run --rm \
-        -e AWS_ACCESS_KEY_ID=${V2_AWS_ACCESS_KEY_ID} \
-        -e AWS_SECRET_ACCESS_KEY=${V2_AWS_SECRET_ACCESS_KEY} \
-        -v ${TEMPDIR}:${TEMPDIR}:Z \
-        -v ${SSH_DATA_DIR}:${SSH_DATA_DIR}:Z \
-        ${CONTAINER_IMAGE_CLOUD_TOOLS} aws --region $AWS_REGION --output json --color on"
-else
-    echo "Using pre-installed 'aws' from the system"
-    AWS_CMD="aws --region $AWS_REGION --output json --color on"
-fi
-$AWS_CMD --version
-
-
-# Remove tagged and old enough instances
-INSTANCES=$(${AWS_CMD} ec2 describe-instances | jq -c '.Reservations[].Instances[]|try {"Tag": .Tags[],"InstanceId": .InstanceId,"LaunchTime": .LaunchTime}')
-
-for instance in ${INSTANCES}; do
-	TAG=$(echo "${instance}" | jq '.Tag.Key' | tr -d '"')
-	TAG_VALUE=$(echo "${instance}" | jq '.Tag.Value' | tr -d '"')
-	INSTANCE_ID=$(echo "${instance}" | jq '.InstanceId' | tr -d '"')
-	LAUNCH_TIME=$(echo "${instance}" | jq '.LaunchTime' | tr -d '"')
-
-	if [[ ${TAG} == "gitlab-ci-test" && ${TAG_VALUE} == "true" ]]; then	
-		if [[ $(date -d "${LAUNCH_TIME}" +%s) -lt "${DELETE_TIME}" ]]; then
-			$AWS_CMD ec2 terminate-instances --instance-id "${INSTANCE_ID}"
-			echo "The instance with id ${INSTANCE_ID} was terminated"
-        	else
-        		echo "The instance with id ${INSTANCE_ID} was launched less than ${HOURS_BACK} hours ago"
-		fi
-	fi
-done
-
-
-# Remove tagged and old enough images
-IMAGES=$($AWS_CMD ec2 describe-images --owner self | jq -c '.Images[] | try {"Tag": .Tags[], "ImageId": .ImageId, "CreationDate": .CreationDate}')
-
-for image in ${IMAGES}; do
-	TAG=$(echo "${image}" | jq '.Tag.Key' | tr -d '"')
-	TAG_VALUE=$(echo "${image}" | jq '.Tag.Value' | tr -d '"')
-	IMAGE_ID=$(echo "${image}" | jq '.ImageId' | tr -d '"')
-	CREATION_DATE=$(echo "${image}" | jq '.CreationDate' | tr -d '"')
-
-	if [[ ${TAG} == "gitlab-ci-test" && ${TAG_VALUE} == "true" ]]; then
-		if [[ $(date -d "${CREATION_DATE}" +%s) -lt "${DELETE_TIME}" ]]; then
-			$AWS_CMD ec2 deregister-image --image-id "${IMAGE_ID}"
-			echo "The image with id ${IMAGE_ID} was deregistered"
-		else
-			echo "The image with id ${IMAGE_ID} was created less than ${HOURS_BACK} hours ago"
-		fi
-	fi
-done
-
-# Remove tagged and old enough snapshots
-SNAPSHOTS=$($AWS_CMD --color on ec2 describe-snapshots --owner self | jq -c '.Snapshots[] | try {"Tag": .Tags[], "SnapshotId": .SnapshotId, "StartTime": .StartTime}')
-
-for snapshot in ${SNAPSHOTS}; do
-	TAG=$(echo "${snapshot}" | jq '.Tag.Key' | tr -d '"')
-	TAG_VALUE=$(echo "${snapshot}" | jq '.Tag.Value' | tr -d '"')
-	SNAPSHOT_ID=$(echo "${snapshot}" | jq '.SnapshotId' | tr -d '"')
-	START_TIME=$(echo "${snapshot}" | jq '.StartTime' | tr -d '"')
-
-	if [[ ${TAG} == "gitlab-ci-test" && ${TAG_VALUE} == "true" ]]; then
-		if [[ $(date -d "${START_TIME}" +%s) -lt "${DELETE_TIME}" ]]; then
-			$AWS_CMD ec2 delete-snapshot --snapshot-id "${SNAPSHOT_ID}"
-			echo "The snapshot with id ${SNAPSHOT_ID} was deleted"
-		else
-			echo "The snapshot with id ${SNAPSHOT_ID} was created less than ${HOURS_BACK} hours ago"
-		fi
-	fi
-done
-
-# Remove tagged and old enough s3 objects
-OBJECTS=$($AWS_CMD s3api list-objects --bucket "${AWS_BUCKET}" | jq -c .Contents[])
-
-for object in ${OBJECTS}; do
-        LAST_MODIFIED=$(echo "${object}" | jq '.LastModified' | tr -d '"')
-        OBJECT_KEY=$(echo "${object}" | jq '.Key' | tr -d '"')
-
-        if [[ $(date -d "${LAST_MODIFIED}" +%s) -lt ${DELETE_TIME} ]]; then
-                TAG=$($AWS_CMD s3api get-object-tagging --bucket "${AWS_BUCKET}" --key "${OBJECT_KEY}" | jq .TagSet[0].Key | tr -d '"')
-                TAG_VALUE=$($AWS_CMD s3api get-object-tagging --bucket "${AWS_BUCKET}" --key "${OBJECT_KEY}" | jq .TagSet[0].Value | tr -d '"')
-
-                if [[ ${TAG} == "gitlab-ci-test" && ${TAG_VALUE} == "true" ]]; then
-                        $AWS_CMD s3 rm "s3://${AWS_BUCKET}/${OBJECT_KEY}"
-                        echo "The object with key ${OBJECT_KEY} was removed"
-                fi
-        fi
-done
-
-#---------------------------------------------------------------
-#                       GCP cleanup
-#---------------------------------------------------------------
-
-greenprint "starting gcp cleanup"
-
-# We need Google Gloud SDK to comunicate with gcp
-if ! hash gcloud; then
-  echo "Using 'gcloud' from a container"
-  sudo ${CONTAINER_RUNTIME} pull ${CONTAINER_IMAGE_CLOUD_TOOLS}
-
-  # directory mounted to the container, in which gcloud stores the credentials after logging in
-  GCP_CMD_CREDS_DIR="${TEMPDIR}/gcloud_credentials"
-  mkdir "${GCP_CMD_CREDS_DIR}"
-
-  GCP_CMD="sudo ${CONTAINER_RUNTIME} run --rm \
-    -v ${GCP_CMD_CREDS_DIR}:/root/.config/gcloud:Z \
-    -v ${GOOGLE_APPLICATION_CREDENTIALS}:${GOOGLE_APPLICATION_CREDENTIALS}:Z \
-    -v ${TEMPDIR}:${TEMPDIR}:Z \
-    ${CONTAINER_IMAGE_CLOUD_TOOLS} gcloud --format=json"
-else
-  echo "Using pre-installed 'gcloud' from the system"
-  GCP_CMD="gcloud --format=json --quiet"
-fi
-$GCP_CMD --version
-
-# Authenticate
-$GCP_CMD auth activate-service-account --key-file "$GOOGLE_APPLICATION_CREDENTIALS"
-# Extract and set the default project to be used for commands
-GCP_PROJECT=$(jq -r '.project_id' "$GOOGLE_APPLICATION_CREDENTIALS")
-$GCP_CMD config set project "$GCP_PROJECT"
-
-# List tagged intances and remove the old enough ones
-INSTANCES=$($GCP_CMD compute instances list --filter='labels.gitlab-ci-test:true' \
-	| jq -c '.[] | {"name": .name, "creationTimestamp": .creationTimestamp, "zone": .zone}')
-
-for instance in ${INSTANCES}; do                
-	CREATION_TIME=$(echo "${instance}" | jq '.creationTimestamp' | tr -d '"')
-
-        if [[ $(date -d "${CREATION_TIME}" +%s) -lt ${DELETE_TIME} ]]; then
-                ZONE=$(echo "${instance}" | jq '.zone' | awk -F / '{print $NF}' | tr -d '"')
-                NAME=$(echo "${instance}" | jq '.name' | tr -d '"')
-                $GCP_CMD compute instances delete --zone="$ZONE" "$NAME"
-                echo "deleted instance: ${NAME}"
-        fi
-done
-
-# List tagged images and remove the old enough ones
-IMAGES=$($GCP_CMD compute images list --filter='labels.gitlab-ci-test:true' \
-	| jq -c '.[] | {"name": .name, "creationTimestamp": .creationTimestamp}')
-
-for image in $IMAGES; do
-        CREATION_TIME=$(echo "${image}" | jq '.creationTimestamp' | tr -d '"')
-
-        if [[ $(date -d "${CREATION_TIME}" +%s) -lt ${DELETE_TIME} ]]; then
-                NAME=$(echo "${image}" | jq '.name' | tr -d '"')
-                $GCP_CMD compute images delete "$NAME"
-                echo "deleted image: ${NAME}"        
-	fi
-done
-
-
-#---------------------------------------------------------------
-#                       vmware cleanup
-#---------------------------------------------------------------
-
-greenprint "starting vmware cleanup"
-
-GOVC_CMD=/tmp/govc
-
-# We need govc to talk to vSphere
-if ! hash govc; then
-    greenprint "Installing govc"
-    pushd /tmp || exit
-        curl -Ls --retry 5 --output govc.gz \
-            https://github.com/vmware/govmomi/releases/download/v0.24.0/govc_linux_amd64.gz
-        gunzip -f govc.gz
-        chmod +x /tmp/govc
-        $GOVC_CMD version
-    popd || exit
-fi
-
-GOVC_AUTH="${GOVMOMI_USERNAME}:${GOVMOMI_PASSWORD}@${GOVMOMI_URL}"
-
-TAGGED=$($GOVC_CMD tags.attached.ls -u "${GOVC_AUTH}" -k "gitlab-ci-test" | xargs -r ${GOVC_CMD} ls -u "${GOVC_AUTH}" -k -L)
-
-for vm in $TAGGED; do
-	# Could use JSON output, but it takes much longer, as it includes more properties
-	CREATION_TIME=$($GOVC_CMD vm.info -u "${GOVC_AUTH}" -k "${vm}" | awk '$1 ~ /^ *Boot/ { print $3 " " $4 $5 }')
-	
-	if [[ $(date -d "${CREATION_TIME}" +%s) -lt ${DELETE_TIME} ]]; then
-                $GOVC_CMD vm.destroy -u "${GOVC_AUTH}" -k "${vm}"
-                echo "destroyed vm: ${vm}"
-	fi
-done
+./cloud-cleaner/cloud_cleaner.sh
\ No newline at end of file
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/chunkwriting.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/chunkwriting.go
index e6bdeebcf..b7dc0d739 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/chunkwriting.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/chunkwriting.go
@@ -56,7 +56,6 @@ func copyFromReader(ctx context.Context, from io.Reader, to blockWriter, o Uploa
 	}
 	// If the error is not EOF, then we have a problem.
 	if err != nil && !errors.Is(err, io.EOF) {
-		cp.wg.Wait()
 		return nil, err
 	}
 
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_blob.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_blob.go
index 008f0822f..6f453e669 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_blob.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_blob.go
@@ -139,22 +139,22 @@ func (b BlobURL) Delete(ctx context.Context, deleteOptions DeleteSnapshotsOption
 	return b.blobClient.Delete(ctx, nil, nil, nil, ac.LeaseAccessConditions.pointers(), deleteOptions,
 		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag,
 		nil, // Blob ifTags
-		nil, BlobDeleteNone)
+		nil)
 }
 
 // SetTags operation enables users to set tags on a blob or specific blob version, but not snapshot.
 // Each call to this operation replaces all existing tags attached to the blob.
 // To remove all tags from the blob, call this operation with no tags set.
 // https://docs.microsoft.com/en-us/rest/api/storageservices/set-blob-tags
-func (b BlobURL) SetTags(ctx context.Context, transactionalContentMD5 []byte, transactionalContentCrc64 []byte, ifTags *string, blobTagsMap BlobTagsMap) (*BlobSetTagsResponse, error) {
+func (b BlobURL) SetTags(ctx context.Context, timeout *int32, versionID *string, transactionalContentMD5 []byte, transactionalContentCrc64 []byte, requestID *string, ifTags *string, blobTagsMap BlobTagsMap) (*BlobSetTagsResponse, error) {
 	tags := SerializeBlobTags(blobTagsMap)
-	return b.blobClient.SetTags(ctx, nil, nil, transactionalContentMD5, transactionalContentCrc64, nil, ifTags, nil, &tags)
+	return b.blobClient.SetTags(ctx, timeout, versionID, transactionalContentMD5, transactionalContentCrc64, requestID, ifTags, &tags)
 }
 
 // GetTags operation enables users to get tags on a blob or specific blob version, or snapshot.
 // https://docs.microsoft.com/en-us/rest/api/storageservices/get-blob-tags
-func (b BlobURL) GetTags(ctx context.Context, ifTags *string) (*BlobTags, error) {
-	return b.blobClient.GetTags(ctx, nil, nil, nil, nil, ifTags, nil)
+func (b BlobURL) GetTags(ctx context.Context, timeout *int32, requestID *string, snapshot *string, versionID *string, ifTags *string) (*BlobTags, error) {
+	return b.blobClient.GetTags(ctx, timeout, requestID, snapshot, versionID, ifTags)
 }
 
 // Undelete restores the contents and metadata of a soft-deleted blob and any associated soft-deleted snapshots.
@@ -173,8 +173,7 @@ func (b BlobURL) Undelete(ctx context.Context) (*BlobUndeleteResponse, error) {
 func (b BlobURL) SetTier(ctx context.Context, tier AccessTierType, lac LeaseAccessConditions) (*BlobSetTierResponse, error) {
 	return b.blobClient.SetTier(ctx, tier, nil,
 		nil, // Blob versioning
-		nil, RehydratePriorityNone, nil, lac.pointers(),
-		nil) // Blob ifTags
+		nil, RehydratePriorityNone, nil, lac.pointers())
 }
 
 // GetProperties returns the blob's properties.
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_block_blob.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_block_blob.go
index 7775559cc..c47ed81b9 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_block_blob.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_block_blob.go
@@ -154,22 +154,6 @@ func (bb BlockBlobURL) CopyFromURL(ctx context.Context, source url.URL, metadata
 		nil, // Blob ifTags
 		dstLeaseID, nil, srcContentMD5,
 		blobTagsString, // Blob tags
+		nil,            // seal Blob
 	)
 }
-
-// PutBlobFromURL synchronously creates a new Block Blob with data from the source URL up to a max length of 256MB.
-// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/put-blob-from-url.
-func (bb BlockBlobURL) PutBlobFromURL(ctx context.Context, h BlobHTTPHeaders, source url.URL, metadata Metadata, srcac ModifiedAccessConditions, dstac BlobAccessConditions, srcContentMD5 []byte, dstContentMD5 []byte, tier AccessTierType, blobTagsMap BlobTagsMap, cpk ClientProvidedKeyOptions) (*BlockBlobPutBlobFromURLResponse, error) {
-
-	srcIfModifiedSince, srcIfUnmodifiedSince, srcIfMatchETag, srcIfNoneMatchETag := srcac.pointers()
-	dstIfModifiedSince, dstIfUnmodifiedSince, dstIfMatchETag, dstIfNoneMatchETag := dstac.ModifiedAccessConditions.pointers()
-	dstLeaseID := dstac.LeaseAccessConditions.pointers()
-	blobTagsString := SerializeBlobTagsHeader(blobTagsMap)
-
-	return bb.bbClient.PutBlobFromURL(ctx, 0, source.String(), nil, nil,
-		&h.ContentType, &h.ContentEncoding, &h.ContentLanguage, dstContentMD5, &h.CacheControl,
-		metadata, dstLeaseID, &h.ContentDisposition, cpk.EncryptionKey, cpk.EncryptionKeySha256,
-		cpk.EncryptionAlgorithm, cpk.EncryptionScope, tier, dstIfModifiedSince, dstIfUnmodifiedSince,
-		dstIfMatchETag, dstIfNoneMatchETag, nil, srcIfModifiedSince, srcIfUnmodifiedSince,
-		srcIfMatchETag, srcIfNoneMatchETag, nil, nil, srcContentMD5, blobTagsString, nil)
-}
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_page_blob.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_page_blob.go
index 624b144b7..d02eff48d 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_page_blob.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/url_page_blob.go
@@ -126,7 +126,7 @@ func (pb PageBlobURL) ClearPages(ctx context.Context, offset int64, count int64,
 		cpk.EncryptionKey, cpk.EncryptionKeySha256, cpk.EncryptionAlgorithm, // CPK
 		cpk.EncryptionScope, // CPK-N
 		ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan,
-		ifSequenceNumberEqual, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil, nil)
+		ifSequenceNumberEqual, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
 }
 
 // GetPageRanges returns the list of valid page ranges for a page blob or snapshot of a page blob.
@@ -175,7 +175,7 @@ func (pb PageBlobURL) Resize(ctx context.Context, size int64, ac BlobAccessCondi
 	return pb.pbClient.Resize(ctx, size, nil, ac.LeaseAccessConditions.pointers(),
 		cpk.EncryptionKey, cpk.EncryptionKeySha256, cpk.EncryptionAlgorithm, // CPK
 		cpk.EncryptionScope, // CPK-N
-		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil, nil)
+		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
 }
 
 // UpdateSequenceNumber sets the page blob's sequence number.
@@ -188,7 +188,7 @@ func (pb PageBlobURL) UpdateSequenceNumber(ctx context.Context, action SequenceN
 	ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch := ac.ModifiedAccessConditions.pointers()
 	return pb.pbClient.UpdateSequenceNumber(ctx, action, nil,
 		ac.LeaseAccessConditions.pointers(), ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch,
-		nil, sn, nil)
+		sn, nil)
 }
 
 // StartCopyIncremental begins an operation to start an incremental copy from one page blob's snapshot to this page blob.
@@ -202,7 +202,7 @@ func (pb PageBlobURL) StartCopyIncremental(ctx context.Context, source url.URL,
 	qp.Set("snapshot", snapshot)
 	source.RawQuery = qp.Encode()
 	return pb.pbClient.CopyIncremental(ctx, source.String(), nil,
-		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil, nil)
+		ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
 }
 
 func (pr PageRange) pointers() *string {
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/version.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/version.go
index 1df7e096a..287e1e4b8 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/version.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/version.go
@@ -1,3 +1,3 @@
 package azblob
 
-const serviceLibVersion = "0.14"
+const serviceLibVersion = "0.13"
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zc_policy_request_log.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zc_policy_request_log.go
index ddc83cc78..29a99a844 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zc_policy_request_log.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zc_policy_request_log.go
@@ -18,11 +18,6 @@ type RequestLogOptions struct {
 	// LogWarningIfTryOverThreshold logs a warning if a tried operation takes longer than the specified
 	// duration (-1=no logging; 0=default threshold).
 	LogWarningIfTryOverThreshold time.Duration
-
-	// SyslogDisabled is a flag to check if logging to Syslog/Windows-Event-Logger is enabled or not
-	// We by default print to Syslog/Windows-Event-Logger.
-	// If SyslogDisabled is not provided explicitly, the default value will be false.
-	SyslogDisabled bool
 }
 
 func (o RequestLogOptions) defaults() RequestLogOptions {
@@ -64,7 +59,7 @@ func NewRequestLogPolicyFactory(o RequestLogOptions) pipeline.Factory {
 			// If the response took too long, we'll upgrade to warning.
 			if o.LogWarningIfTryOverThreshold > 0 && tryDuration > o.LogWarningIfTryOverThreshold {
 				// Log a warning if the try duration exceeded the specified threshold
-				logLevel, forceLog = pipeline.LogWarning, !o.SyslogDisabled
+				logLevel, forceLog = pipeline.LogWarning, true
 			}
 
 			var sc int
@@ -78,9 +73,8 @@ func NewRequestLogPolicyFactory(o RequestLogOptions) pipeline.Factory {
 				}
 			}
 
-			if sc == 0 || ((sc >= 400 && sc <= 499) && sc != http.StatusNotFound && sc != http.StatusConflict &&
-				sc != http.StatusPreconditionFailed && sc != http.StatusRequestedRangeNotSatisfiable) || (sc >= 500 && sc <= 599) {
-				logLevel, forceLog = pipeline.LogError, !o.SyslogDisabled // Promote to Error any 4xx (except those listed is an error) or any 5xx
+			if sc == 0 || ((sc >= 400 && sc <= 499) && sc != http.StatusNotFound && sc != http.StatusConflict && sc != http.StatusPreconditionFailed && sc != http.StatusRequestedRangeNotSatisfiable) || (sc >= 500 && sc <= 599) {
+				logLevel, forceLog = pipeline.LogError, true // Promote to Error any 4xx (except those listed is an error) or any 5xx
 			} else {
 				// For other status codes, we leave the level as is.
 			}
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zc_policy_unique_request_id.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zc_policy_unique_request_id.go
index 1f7817d2d..db8cee7b4 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zc_policy_unique_request_id.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zc_policy_unique_request_id.go
@@ -3,7 +3,6 @@ package azblob
 import (
 	"context"
 	"errors"
-
 	"github.com/Azure/azure-pipeline-go/pipeline"
 )
 
@@ -22,9 +21,11 @@ func NewUniqueRequestIDPolicyFactory() pipeline.Factory {
 			resp, err := next.Do(ctx, request)
 
 			if err == nil && resp != nil {
-				crId := resp.Response().Header.Get(xMsClientRequestID)
-				if crId != "" && crId != id {
-					err = errors.New("client Request ID from request and response does not match")
+				val := resp.Response().Header.Values(xMsClientRequestID)
+				if len(val) > 0 {
+					if val[0] != id {
+						err = errors.New("client Request ID from request and response does not match")
+					}
 				}
 			}
 
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_blob.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_blob.go
index 1b222b6b1..036bbfcfa 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_blob.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_blob.go
@@ -366,15 +366,16 @@ func (client blobClient) changeLeaseResponder(resp pipeline.Response) (pipeline.
 // only succeeds if the resource's lease is active and matches this ID. requestID is provides a client-generated,
 // opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is
 // enabled. sourceContentMD5 is specify the md5 calculated for the range of bytes that must be read from the copy
-// source. blobTagsString is optional.  Used to set blob tags in various blob operations.
-func (client blobClient) CopyFromURL(ctx context.Context, copySource string, timeout *int32, metadata map[string]string, tier AccessTierType, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, leaseID *string, requestID *string, sourceContentMD5 []byte, blobTagsString *string) (*BlobCopyFromURLResponse, error) {
+// source. blobTagsString is optional.  Used to set blob tags in various blob operations. sealBlob is overrides the
+// sealed state of the destination blob.  Service version 2019-12-12 and newer.
+func (client blobClient) CopyFromURL(ctx context.Context, copySource string, timeout *int32, metadata map[string]string, tier AccessTierType, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, leaseID *string, requestID *string, sourceContentMD5 []byte, blobTagsString *string, sealBlob *bool) (*BlobCopyFromURLResponse, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.copyFromURLPreparer(copySource, timeout, metadata, tier, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, leaseID, requestID, sourceContentMD5, blobTagsString)
+	req, err := client.copyFromURLPreparer(copySource, timeout, metadata, tier, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, leaseID, requestID, sourceContentMD5, blobTagsString, sealBlob)
 	if err != nil {
 		return nil, err
 	}
@@ -386,7 +387,7 @@ func (client blobClient) CopyFromURL(ctx context.Context, copySource string, tim
 }
 
 // copyFromURLPreparer prepares the CopyFromURL request.
-func (client blobClient) copyFromURLPreparer(copySource string, timeout *int32, metadata map[string]string, tier AccessTierType, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, leaseID *string, requestID *string, sourceContentMD5 []byte, blobTagsString *string) (pipeline.Request, error) {
+func (client blobClient) copyFromURLPreparer(copySource string, timeout *int32, metadata map[string]string, tier AccessTierType, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, leaseID *string, requestID *string, sourceContentMD5 []byte, blobTagsString *string, sealBlob *bool) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("PUT", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -445,6 +446,9 @@ func (client blobClient) copyFromURLPreparer(copySource string, timeout *int32,
 	if blobTagsString != nil {
 		req.Header.Set("x-ms-tags", *blobTagsString)
 	}
+	if sealBlob != nil {
+		req.Header.Set("x-ms-seal-blob", strconv.FormatBool(*sealBlob))
+	}
 	req.Header.Set("x-ms-requires-sync", "true")
 	return req, nil
 }
@@ -595,16 +599,15 @@ func (client blobClient) createSnapshotResponder(resp pipeline.Response) (pipeli
 // blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
 // ifTags is specify a SQL where clause on blob tags to operate only on blobs with a matching value. requestID is
 // provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when
-// storage analytics logging is enabled. blobDeleteType is optional.  Only possible value is 'permanent', which
-// specifies to permanently delete a blob if blob soft delete is enabled.
-func (client blobClient) Delete(ctx context.Context, snapshot *string, versionID *string, timeout *int32, leaseID *string, deleteSnapshots DeleteSnapshotsOptionType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string, blobDeleteType BlobDeleteType) (*BlobDeleteResponse, error) {
+// storage analytics logging is enabled.
+func (client blobClient) Delete(ctx context.Context, snapshot *string, versionID *string, timeout *int32, leaseID *string, deleteSnapshots DeleteSnapshotsOptionType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (*BlobDeleteResponse, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.deletePreparer(snapshot, versionID, timeout, leaseID, deleteSnapshots, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, requestID, blobDeleteType)
+	req, err := client.deletePreparer(snapshot, versionID, timeout, leaseID, deleteSnapshots, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, requestID)
 	if err != nil {
 		return nil, err
 	}
@@ -616,7 +619,7 @@ func (client blobClient) Delete(ctx context.Context, snapshot *string, versionID
 }
 
 // deletePreparer prepares the Delete request.
-func (client blobClient) deletePreparer(snapshot *string, versionID *string, timeout *int32, leaseID *string, deleteSnapshots DeleteSnapshotsOptionType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string, blobDeleteType BlobDeleteType) (pipeline.Request, error) {
+func (client blobClient) deletePreparer(snapshot *string, versionID *string, timeout *int32, leaseID *string, deleteSnapshots DeleteSnapshotsOptionType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("DELETE", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -631,9 +634,6 @@ func (client blobClient) deletePreparer(snapshot *string, versionID *string, tim
 	if timeout != nil {
 		params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
 	}
-	if blobDeleteType != BlobDeleteNone {
-		params.Set("deletetype", string(blobDeleteType))
-	}
 	req.URL.RawQuery = params.Encode()
 	if leaseID != nil {
 		req.Header.Set("x-ms-lease-id", *leaseID)
@@ -1020,16 +1020,15 @@ func (client blobClient) getPropertiesResponder(resp pipeline.Response) (pipelin
 // href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating
 // a Snapshot of a Blob.</a> versionID is the version id parameter is an opaque DateTime value that, when present,
 // specifies the version of the blob to operate on. It's for service version 2019-10-10 and newer. ifTags is specify a
-// SQL where clause on blob tags to operate only on blobs with a matching value. leaseID is if specified, the operation
-// only succeeds if the resource's lease is active and matches this ID.
-func (client blobClient) GetTags(ctx context.Context, timeout *int32, requestID *string, snapshot *string, versionID *string, ifTags *string, leaseID *string) (*BlobTags, error) {
+// SQL where clause on blob tags to operate only on blobs with a matching value.
+func (client blobClient) GetTags(ctx context.Context, timeout *int32, requestID *string, snapshot *string, versionID *string, ifTags *string) (*BlobTags, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.getTagsPreparer(timeout, requestID, snapshot, versionID, ifTags, leaseID)
+	req, err := client.getTagsPreparer(timeout, requestID, snapshot, versionID, ifTags)
 	if err != nil {
 		return nil, err
 	}
@@ -1041,7 +1040,7 @@ func (client blobClient) GetTags(ctx context.Context, timeout *int32, requestID
 }
 
 // getTagsPreparer prepares the GetTags request.
-func (client blobClient) getTagsPreparer(timeout *int32, requestID *string, snapshot *string, versionID *string, ifTags *string, leaseID *string) (pipeline.Request, error) {
+func (client blobClient) getTagsPreparer(timeout *int32, requestID *string, snapshot *string, versionID *string, ifTags *string) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("GET", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -1065,9 +1064,6 @@ func (client blobClient) getTagsPreparer(timeout *int32, requestID *string, snap
 	if ifTags != nil {
 		req.Header.Set("x-ms-if-tags", *ifTags)
 	}
-	if leaseID != nil {
-		req.Header.Set("x-ms-lease-id", *leaseID)
-	}
 	return req, nil
 }
 
@@ -1096,111 +1092,107 @@ func (client blobClient) getTagsResponder(resp pipeline.Response) (pipeline.Resp
 	return result, nil
 }
 
-// todo funky quick query code
-// // Query the Query operation enables users to select/project on blob data by providing simple query expressions.
-// //
-// // snapshot is the snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to
-// // retrieve. For more information on working with blob snapshots, see <a
-// // href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating
-// // a Snapshot of a Blob.</a> timeout is the timeout parameter is expressed in seconds. For more information, see <a
-// // href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
-// // Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
-// // lease is active and matches this ID. encryptionKey is optional. Specifies the encryption key to use to encrypt the
-// // data provided in the request. If not specified, encryption is performed with the root account encryption key.  For
-// // more information, see Encryption at Rest for Azure Storage Services. encryptionKeySha256 is the SHA-256 hash of the
-// // provided encryption key. Must be provided if the x-ms-encryption-key header is provided. encryptionAlgorithm is the
-// // algorithm used to produce the encryption key hash. Currently, the only accepted value is "AES256". Must be provided
-// // if the x-ms-encryption-key header is provided. ifModifiedSince is specify this header value to operate only on a
-// // blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
-// // operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value
-// // to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
-// // without a matching value. ifTags is specify a SQL where clause on blob tags to operate only on blobs with a matching
-// // value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the
-// // analytics logs when storage analytics logging is enabled.
-// func (client blobClient) Query(ctx context.Context, snapshot *string, timeout *int32, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (*QueryResponse, error) {
-// 	if err := validate([]validation{
-// 		{targetValue: timeout,
-// 			constraints: []constraint{{target: "timeout", name: null, rule: false,
-// 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
-// 		return nil, err
-// 	}
-// 	req, err := client.queryPreparer(snapshot, timeout, leaseID, encryptionKey, encryptionKeySha256, encryptionAlgorithm, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, requestID)
-// 	if err != nil {
-// 		return nil, err
-// 	}
-// 	resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.queryResponder}, req)
-// 	if err != nil {
-// 		return nil, err
-// 	}
-// 	return resp.(*QueryResponse), err
-// }
+// TODO funky quick query code
+//// Query the Query operation enables users to select/project on blob data by providing simple query expressions.
+////
+//// snapshot is the snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to
+//// retrieve. For more information on working with blob snapshots, see <a
+//// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating
+//// a Snapshot of a Blob.</a> timeout is the timeout parameter is expressed in seconds. For more information, see <a
+//// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
+//// Timeouts for Blob Service Operations.</a> leaseID is if specified, the operation only succeeds if the resource's
+//// lease is active and matches this ID. encryptionKey is optional. Specifies the encryption key to use to encrypt the
+//// data provided in the request. If not specified, encryption is performed with the root account encryption key.  For
+//// more information, see Encryption at Rest for Azure Storage Services. encryptionKeySha256 is the SHA-256 hash of the
+//// provided encryption key. Must be provided if the x-ms-encryption-key header is provided. encryptionAlgorithm is the
+//// algorithm used to produce the encryption key hash. Currently, the only accepted value is "AES256". Must be provided
+//// if the x-ms-encryption-key header is provided. ifModifiedSince is specify this header value to operate only on a
+//// blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
+//// operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value
+//// to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
+//// without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
+//// recorded in the analytics logs when storage analytics logging is enabled.
+//func (client blobClient) Query(ctx context.Context, snapshot *string, timeout *int32, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*QueryResponse, error) {
+//	if err := validate([]validation{
+//		{targetValue: timeout,
+//			constraints: []constraint{{target: "timeout", name: null, rule: false,
+//				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
+//		return nil, err
+//	}
+//	req, err := client.queryPreparer(snapshot, timeout, leaseID, encryptionKey, encryptionKeySha256, encryptionAlgorithm, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
+//	if err != nil {
+//		return nil, err
+//	}
+//	resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.queryResponder}, req)
+//	if err != nil {
+//		return nil, err
+//	}
+//	return resp.(*QueryResponse), err
+//}
 //
-// // queryPreparer prepares the Query request.
-// func (client blobClient) queryPreparer(snapshot *string, timeout *int32, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (pipeline.Request, error) {
-// 	req, err := pipeline.NewRequest("POST", client.url, nil)
-// 	if err != nil {
-// 		return req, pipeline.NewError(err, "failed to create request")
-// 	}
-// 	params := req.URL.Query()
-// 	if snapshot != nil && len(*snapshot) > 0 {
-// 		params.Set("snapshot", *snapshot)
-// 	}
-// 	if timeout != nil {
-// 		params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
-// 	}
-// 	params.Set("comp", "query")
-// 	req.URL.RawQuery = params.Encode()
-// 	if leaseID != nil {
-// 		req.Header.Set("x-ms-lease-id", *leaseID)
-// 	}
-// 	if encryptionKey != nil {
-// 		req.Header.Set("x-ms-encryption-key", *encryptionKey)
-// 	}
-// 	if encryptionKeySha256 != nil {
-// 		req.Header.Set("x-ms-encryption-key-sha256", *encryptionKeySha256)
-// 	}
-// 	if encryptionAlgorithm != EncryptionAlgorithmNone {
-// 		req.Header.Set("x-ms-encryption-algorithm", string(encryptionAlgorithm))
-// 	}
-// 	if ifModifiedSince != nil {
-// 		req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
-// 	}
-// 	if ifUnmodifiedSince != nil {
-// 		req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
-// 	}
-// 	if ifMatch != nil {
-// 		req.Header.Set("If-Match", string(*ifMatch))
-// 	}
-// 	if ifNoneMatch != nil {
-// 		req.Header.Set("If-None-Match", string(*ifNoneMatch))
-// 	}
-// 	if ifTags != nil {
-// 		req.Header.Set("x-ms-if-tags", *ifTags)
-// 	}
-// 	req.Header.Set("x-ms-version", ServiceVersion)
-// 	if requestID != nil {
-// 		req.Header.Set("x-ms-client-request-id", *requestID)
-// 	}
-// 	b, err := xml.Marshal(queryRequest)
-// 	if err != nil {
-// 		return req, pipeline.NewError(err, "failed to marshal request body")
-// 	}
-// 	req.Header.Set("Content-Type", "application/xml")
-// 	err = req.SetBody(bytes.NewReader(b))
-// 	if err != nil {
-// 		return req, pipeline.NewError(err, "failed to set request body")
-// 	}
-// 	return req, nil
-// }
+//// queryPreparer prepares the Query request.
+//func (client blobClient) queryPreparer(snapshot *string, timeout *int32, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
+//	req, err := pipeline.NewRequest("POST", client.url, nil)
+//	if err != nil {
+//		return req, pipeline.NewError(err, "failed to create request")
+//	}
+//	params := req.URL.Query()
+//	if snapshot != nil && len(*snapshot) > 0 {
+//		params.Set("snapshot", *snapshot)
+//	}
+//	if timeout != nil {
+//		params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
+//	}
+//	params.Set("comp", "query")
+//	req.URL.RawQuery = params.Encode()
+//	if leaseID != nil {
+//		req.Header.Set("x-ms-lease-id", *leaseID)
+//	}
+//	if encryptionKey != nil {
+//		req.Header.Set("x-ms-encryption-key", *encryptionKey)
+//	}
+//	if encryptionKeySha256 != nil {
+//		req.Header.Set("x-ms-encryption-key-sha256", *encryptionKeySha256)
+//	}
+//	if encryptionAlgorithm != EncryptionAlgorithmNone {
+//		req.Header.Set("x-ms-encryption-algorithm", string(encryptionAlgorithm))
+//	}
+//	if ifModifiedSince != nil {
+//		req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
+//	}
+//	if ifUnmodifiedSince != nil {
+//		req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
+//	}
+//	if ifMatch != nil {
+//		req.Header.Set("If-Match", string(*ifMatch))
+//	}
+//	if ifNoneMatch != nil {
+//		req.Header.Set("If-None-Match", string(*ifNoneMatch))
+//	}
+//	req.Header.Set("x-ms-version", ServiceVersion)
+//	if requestID != nil {
+//		req.Header.Set("x-ms-client-request-id", *requestID)
+//	}
+//	b, err := xml.Marshal(queryRequest)
+//	if err != nil {
+//		return req, pipeline.NewError(err, "failed to marshal request body")
+//	}
+//	req.Header.Set("Content-Type", "application/xml")
+//	err = req.SetBody(bytes.NewReader(b))
+//	if err != nil {
+//		return req, pipeline.NewError(err, "failed to set request body")
+//	}
+//	return req, nil
+//}
 //
-// // queryResponder handles the response to the Query request.
-// func (client blobClient) queryResponder(resp pipeline.Response) (pipeline.Response, error) {
-// 	err := validateResponse(resp, http.StatusOK, http.StatusPartialContent)
-// 	if resp == nil {
-// 		return nil, err
-// 	}
-// 	return &QueryResponse{rawResponse: resp.Response()}, err
-// }
+//// queryResponder handles the response to the Query request.
+//func (client blobClient) queryResponder(resp pipeline.Response) (pipeline.Response, error) {
+//	err := validateResponse(resp, http.StatusOK, http.StatusPartialContent)
+//	if resp == nil {
+//		return nil, err
+//	}
+//	return &QueryResponse{rawResponse: resp.Response()}, err
+//}
 
 // ReleaseLease [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete
 // operations
@@ -1280,145 +1272,146 @@ func (client blobClient) releaseLeaseResponder(resp pipeline.Response) (pipeline
 	return &BlobReleaseLeaseResponse{rawResponse: resp.Response()}, err
 }
 
-// Rename rename a blob/file.  By default, the destination is overwritten and if the destination already exists and has
-// a lease the lease is broken.  This operation supports conditional HTTP requests.  For more information, see
-// [Specifying Conditional Headers for Blob Service
-// Operations](https://docs.microsoft.com/en-us/rest/api/storageservices/specifying-conditional-headers-for-blob-service-operations).
-// To fail if the destination already exists, use a conditional request with If-None-Match: "*".
+// TODO funky rename API
+//// Rename rename a blob/file.  By default, the destination is overwritten and if the destination already exists and has
+//// a lease the lease is broken.  This operation supports conditional HTTP requests.  For more information, see
+//// [Specifying Conditional Headers for Blob Service
+//// Operations](https://docs.microsoft.com/en-us/rest/api/storageservices/specifying-conditional-headers-for-blob-service-operations).
+//// To fail if the destination already exists, use a conditional request with If-None-Match: "*".
+////
+//// renameSource is the file or directory to be renamed. The value must have the following format:
+//// "/{filesysystem}/{path}".  If "x-ms-properties" is specified, the properties will overwrite the existing properties;
+//// otherwise, the existing properties will be preserved. timeout is the timeout parameter is expressed in seconds. For
+//// more information, see <a
+//// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
+//// Timeouts for Blob Service Operations.</a> directoryProperties is optional.  User-defined properties to be stored
+//// with the file or directory, in the format of a comma-separated list of name and value pairs "n1=v1, n2=v2, ...",
+//// where each value is base64 encoded. posixPermissions is optional and only valid if Hierarchical Namespace is enabled
+//// for the account. Sets POSIX access permissions for the file owner, the file owning group, and others. Each class may
+//// be granted read, write, or execute permission.  The sticky bit is also supported.  Both symbolic (rwxrw-rw-) and
+//// 4-digit octal notation (e.g. 0766) are supported. posixUmask is only valid if Hierarchical Namespace is enabled for
+//// the account. This umask restricts permission settings for file and directory, and will only be applied when default
+//// Acl does not exist in parent directory. If the umask bit has set, it means that the corresponding permission will be
+//// disabled. Otherwise the corresponding permission will be determined by the permission. A 4-digit octal notation
+//// (e.g. 0022) is supported here. If no umask was specified, a default umask - 0027 will be used. cacheControl is cache
+//// control for given resource contentType is content type for given resource contentEncoding is content encoding for
+//// given resource contentLanguage is content language for given resource contentDisposition is content disposition for
+//// given resource leaseID is if specified, the operation only succeeds if the resource's lease is active and matches
+//// this ID. sourceLeaseID is a lease ID for the source path. If specified, the source path must have an active lease
+//// and the lease ID must match. ifModifiedSince is specify this header value to operate only on a blob if it has been
+//// modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if
+//// it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on blobs
+//// with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
+//// sourceIfModifiedSince is specify this header value to operate only on a blob if it has been modified since the
+//// specified date/time. sourceIfUnmodifiedSince is specify this header value to operate only on a blob if it has not
+//// been modified since the specified date/time. sourceIfMatch is specify an ETag value to operate only on blobs with a
+//// matching value. sourceIfNoneMatch is specify an ETag value to operate only on blobs without a matching value.
+//// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
+//// logs when storage analytics logging is enabled.
+//func (client blobClient) Rename(ctx context.Context, renameSource string, timeout *int32, directoryProperties *string, posixPermissions *string, posixUmask *string, cacheControl *string, contentType *string, contentEncoding *string, contentLanguage *string, contentDisposition *string, leaseID *string, sourceLeaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (*BlobRenameResponse, error) {
+//	if err := validate([]validation{
+//		{targetValue: timeout,
+//			constraints: []constraint{{target: "timeout", name: null, rule: false,
+//				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
+//		return nil, err
+//	}
+//	req, err := client.renamePreparer(renameSource, timeout, directoryProperties, posixPermissions, posixUmask, cacheControl, contentType, contentEncoding, contentLanguage, contentDisposition, leaseID, sourceLeaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, requestID)
+//	if err != nil {
+//		return nil, err
+//	}
+//	resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.renameResponder}, req)
+//	if err != nil {
+//		return nil, err
+//	}
+//	return resp.(*BlobRenameResponse), err
+//}
 //
-// renameSource is the file or directory to be renamed. The value must have the following format:
-// "/{filesysystem}/{path}".  If "x-ms-properties" is specified, the properties will overwrite the existing properties;
-// otherwise, the existing properties will be preserved. timeout is the timeout parameter is expressed in seconds. For
-// more information, see <a
-// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
-// Timeouts for Blob Service Operations.</a> directoryProperties is optional.  User-defined properties to be stored
-// with the file or directory, in the format of a comma-separated list of name and value pairs "n1=v1, n2=v2, ...",
-// where each value is base64 encoded. posixPermissions is optional and only valid if Hierarchical Namespace is enabled
-// for the account. Sets POSIX access permissions for the file owner, the file owning group, and others. Each class may
-// be granted read, write, or execute permission.  The sticky bit is also supported.  Both symbolic (rwxrw-rw-) and
-// 4-digit octal notation (e.g. 0766) are supported. posixUmask is only valid if Hierarchical Namespace is enabled for
-// the account. This umask restricts permission settings for file and directory, and will only be applied when default
-// Acl does not exist in parent directory. If the umask bit has set, it means that the corresponding permission will be
-// disabled. Otherwise the corresponding permission will be determined by the permission. A 4-digit octal notation
-// (e.g. 0022) is supported here. If no umask was specified, a default umask - 0027 will be used. cacheControl is cache
-// control for given resource contentType is content type for given resource contentEncoding is content encoding for
-// given resource contentLanguage is content language for given resource contentDisposition is content disposition for
-// given resource leaseID is if specified, the operation only succeeds if the resource's lease is active and matches
-// this ID. sourceLeaseID is a lease ID for the source path. If specified, the source path must have an active lease
-// and the lease ID must match. ifModifiedSince is specify this header value to operate only on a blob if it has been
-// modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if
-// it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on blobs
-// with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
-// sourceIfModifiedSince is specify this header value to operate only on a blob if it has been modified since the
-// specified date/time. sourceIfUnmodifiedSince is specify this header value to operate only on a blob if it has not
-// been modified since the specified date/time. sourceIfMatch is specify an ETag value to operate only on blobs with a
-// matching value. sourceIfNoneMatch is specify an ETag value to operate only on blobs without a matching value.
-// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
-// logs when storage analytics logging is enabled.
-func (client blobClient) Rename(ctx context.Context, renameSource string, timeout *int32, directoryProperties *string, posixPermissions *string, posixUmask *string, cacheControl *string, contentType *string, contentEncoding *string, contentLanguage *string, contentDisposition *string, leaseID *string, sourceLeaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (*BlobRenameResponse, error) {
-	if err := validate([]validation{
-		{targetValue: timeout,
-			constraints: []constraint{{target: "timeout", name: null, rule: false,
-				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
-		return nil, err
-	}
-	req, err := client.renamePreparer(renameSource, timeout, directoryProperties, posixPermissions, posixUmask, cacheControl, contentType, contentEncoding, contentLanguage, contentDisposition, leaseID, sourceLeaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, requestID)
-	if err != nil {
-		return nil, err
-	}
-	resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.renameResponder}, req)
-	if err != nil {
-		return nil, err
-	}
-	return resp.(*BlobRenameResponse), err
-}
-
-// renamePreparer prepares the Rename request.
-func (client blobClient) renamePreparer(renameSource string, timeout *int32, directoryProperties *string, posixPermissions *string, posixUmask *string, cacheControl *string, contentType *string, contentEncoding *string, contentLanguage *string, contentDisposition *string, leaseID *string, sourceLeaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
-	req, err := pipeline.NewRequest("PUT", client.url, nil)
-	if err != nil {
-		return req, pipeline.NewError(err, "failed to create request")
-	}
-	params := req.URL.Query()
-	if timeout != nil {
-		params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
-	}
-	// if pathRenameMode != PathRenameModeNone {
-	// 	params.Set("mode", string(client.PathRenameMode))
-	// }
-	req.URL.RawQuery = params.Encode()
-	req.Header.Set("x-ms-rename-source", renameSource)
-	if directoryProperties != nil {
-		req.Header.Set("x-ms-properties", *directoryProperties)
-	}
-	if posixPermissions != nil {
-		req.Header.Set("x-ms-permissions", *posixPermissions)
-	}
-	if posixUmask != nil {
-		req.Header.Set("x-ms-umask", *posixUmask)
-	}
-	if cacheControl != nil {
-		req.Header.Set("x-ms-cache-control", *cacheControl)
-	}
-	if contentType != nil {
-		req.Header.Set("x-ms-content-type", *contentType)
-	}
-	if contentEncoding != nil {
-		req.Header.Set("x-ms-content-encoding", *contentEncoding)
-	}
-	if contentLanguage != nil {
-		req.Header.Set("x-ms-content-language", *contentLanguage)
-	}
-	if contentDisposition != nil {
-		req.Header.Set("x-ms-content-disposition", *contentDisposition)
-	}
-	if leaseID != nil {
-		req.Header.Set("x-ms-lease-id", *leaseID)
-	}
-	if sourceLeaseID != nil {
-		req.Header.Set("x-ms-source-lease-id", *sourceLeaseID)
-	}
-	if ifModifiedSince != nil {
-		req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
-	}
-	if ifUnmodifiedSince != nil {
-		req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
-	}
-	if ifMatch != nil {
-		req.Header.Set("If-Match", string(*ifMatch))
-	}
-	if ifNoneMatch != nil {
-		req.Header.Set("If-None-Match", string(*ifNoneMatch))
-	}
-	if sourceIfModifiedSince != nil {
-		req.Header.Set("x-ms-source-if-modified-since", (*sourceIfModifiedSince).In(gmt).Format(time.RFC1123))
-	}
-	if sourceIfUnmodifiedSince != nil {
-		req.Header.Set("x-ms-source-if-unmodified-since", (*sourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123))
-	}
-	if sourceIfMatch != nil {
-		req.Header.Set("x-ms-source-if-match", string(*sourceIfMatch))
-	}
-	if sourceIfNoneMatch != nil {
-		req.Header.Set("x-ms-source-if-none-match", string(*sourceIfNoneMatch))
-	}
-	req.Header.Set("x-ms-version", ServiceVersion)
-	if requestID != nil {
-		req.Header.Set("x-ms-client-request-id", *requestID)
-	}
-	return req, nil
-}
-
-// renameResponder handles the response to the Rename request.
-func (client blobClient) renameResponder(resp pipeline.Response) (pipeline.Response, error) {
-	err := validateResponse(resp, http.StatusOK, http.StatusCreated)
-	if resp == nil {
-		return nil, err
-	}
-	io.Copy(ioutil.Discard, resp.Response().Body)
-	resp.Response().Body.Close()
-	return &BlobRenameResponse{rawResponse: resp.Response()}, err
-}
+//// renamePreparer prepares the Rename request.
+//func (client blobClient) renamePreparer(renameSource string, timeout *int32, directoryProperties *string, posixPermissions *string, posixUmask *string, cacheControl *string, contentType *string, contentEncoding *string, contentLanguage *string, contentDisposition *string, leaseID *string, sourceLeaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
+//	req, err := pipeline.NewRequest("PUT", client.url, nil)
+//	if err != nil {
+//		return req, pipeline.NewError(err, "failed to create request")
+//	}
+//	params := req.URL.Query()
+//	if timeout != nil {
+//		params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
+//	}
+//	if pathRenameMode != PathRenameModeNone {
+//		params.Set("mode", string(client.PathRenameMode))
+//	}
+//	req.URL.RawQuery = params.Encode()
+//	req.Header.Set("x-ms-rename-source", renameSource)
+//	if directoryProperties != nil {
+//		req.Header.Set("x-ms-properties", *directoryProperties)
+//	}
+//	if posixPermissions != nil {
+//		req.Header.Set("x-ms-permissions", *posixPermissions)
+//	}
+//	if posixUmask != nil {
+//		req.Header.Set("x-ms-umask", *posixUmask)
+//	}
+//	if cacheControl != nil {
+//		req.Header.Set("x-ms-cache-control", *cacheControl)
+//	}
+//	if contentType != nil {
+//		req.Header.Set("x-ms-content-type", *contentType)
+//	}
+//	if contentEncoding != nil {
+//		req.Header.Set("x-ms-content-encoding", *contentEncoding)
+//	}
+//	if contentLanguage != nil {
+//		req.Header.Set("x-ms-content-language", *contentLanguage)
+//	}
+//	if contentDisposition != nil {
+//		req.Header.Set("x-ms-content-disposition", *contentDisposition)
+//	}
+//	if leaseID != nil {
+//		req.Header.Set("x-ms-lease-id", *leaseID)
+//	}
+//	if sourceLeaseID != nil {
+//		req.Header.Set("x-ms-source-lease-id", *sourceLeaseID)
+//	}
+//	if ifModifiedSince != nil {
+//		req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
+//	}
+//	if ifUnmodifiedSince != nil {
+//		req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
+//	}
+//	if ifMatch != nil {
+//		req.Header.Set("If-Match", string(*ifMatch))
+//	}
+//	if ifNoneMatch != nil {
+//		req.Header.Set("If-None-Match", string(*ifNoneMatch))
+//	}
+//	if sourceIfModifiedSince != nil {
+//		req.Header.Set("x-ms-source-if-modified-since", (*sourceIfModifiedSince).In(gmt).Format(time.RFC1123))
+//	}
+//	if sourceIfUnmodifiedSince != nil {
+//		req.Header.Set("x-ms-source-if-unmodified-since", (*sourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123))
+//	}
+//	if sourceIfMatch != nil {
+//		req.Header.Set("x-ms-source-if-match", string(*sourceIfMatch))
+//	}
+//	if sourceIfNoneMatch != nil {
+//		req.Header.Set("x-ms-source-if-none-match", string(*sourceIfNoneMatch))
+//	}
+//	req.Header.Set("x-ms-version", ServiceVersion)
+//	if requestID != nil {
+//		req.Header.Set("x-ms-client-request-id", *requestID)
+//	}
+//	return req, nil
+//}
+//
+//// renameResponder handles the response to the Rename request.
+//func (client blobClient) renameResponder(resp pipeline.Response) (pipeline.Response, error) {
+//	err := validateResponse(resp, http.StatusOK, http.StatusCreated)
+//	if resp == nil {
+//		return nil, err
+//	}
+//	io.Copy(ioutil.Discard, resp.Response().Body)
+//	resp.Response().Body.Close()
+//	return &BlobRenameResponse{rawResponse: resp.Response()}, err
+//}
 
 // RenewLease [Update] The Lease Blob operation establishes and manages a lock on a blob for write and delete
 // operations
@@ -1873,16 +1866,15 @@ func (client blobClient) setMetadataResponder(resp pipeline.Response) (pipeline.
 // transactionalContentCrc64 is specify the transactional crc64 for the body, to be validated by the service. requestID
 // is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when
 // storage analytics logging is enabled. ifTags is specify a SQL where clause on blob tags to operate only on blobs
-// with a matching value. leaseID is if specified, the operation only succeeds if the resource's lease is active and
-// matches this ID. tags is blob tags
-func (client blobClient) SetTags(ctx context.Context, timeout *int32, versionID *string, transactionalContentMD5 []byte, transactionalContentCrc64 []byte, requestID *string, ifTags *string, leaseID *string, tags *BlobTags) (*BlobSetTagsResponse, error) {
+// with a matching value. tags is blob tags
+func (client blobClient) SetTags(ctx context.Context, timeout *int32, versionID *string, transactionalContentMD5 []byte, transactionalContentCrc64 []byte, requestID *string, ifTags *string, tags *BlobTags) (*BlobSetTagsResponse, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.setTagsPreparer(timeout, versionID, transactionalContentMD5, transactionalContentCrc64, requestID, ifTags, leaseID, tags)
+	req, err := client.setTagsPreparer(timeout, versionID, transactionalContentMD5, transactionalContentCrc64, requestID, ifTags, tags)
 	if err != nil {
 		return nil, err
 	}
@@ -1894,7 +1886,7 @@ func (client blobClient) SetTags(ctx context.Context, timeout *int32, versionID
 }
 
 // setTagsPreparer prepares the SetTags request.
-func (client blobClient) setTagsPreparer(timeout *int32, versionID *string, transactionalContentMD5 []byte, transactionalContentCrc64 []byte, requestID *string, ifTags *string, leaseID *string, tags *BlobTags) (pipeline.Request, error) {
+func (client blobClient) setTagsPreparer(timeout *int32, versionID *string, transactionalContentMD5 []byte, transactionalContentCrc64 []byte, requestID *string, ifTags *string, tags *BlobTags) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("PUT", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -1921,9 +1913,6 @@ func (client blobClient) setTagsPreparer(timeout *int32, versionID *string, tran
 	if ifTags != nil {
 		req.Header.Set("x-ms-if-tags", *ifTags)
 	}
-	if leaseID != nil {
-		req.Header.Set("x-ms-lease-id", *leaseID)
-	}
 	b, err := xml.Marshal(tags)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to marshal request body")
@@ -1963,16 +1952,15 @@ func (client blobClient) setTagsResponder(resp pipeline.Response) (pipeline.Resp
 // Timeouts for Blob Service Operations.</a> rehydratePriority is optional: Indicates the priority with which to
 // rehydrate an archived blob. requestID is provides a client-generated, opaque value with a 1 KB character limit that
 // is recorded in the analytics logs when storage analytics logging is enabled. leaseID is if specified, the operation
-// only succeeds if the resource's lease is active and matches this ID. ifTags is specify a SQL where clause on blob
-// tags to operate only on blobs with a matching value.
-func (client blobClient) SetTier(ctx context.Context, tier AccessTierType, snapshot *string, versionID *string, timeout *int32, rehydratePriority RehydratePriorityType, requestID *string, leaseID *string, ifTags *string) (*BlobSetTierResponse, error) {
+// only succeeds if the resource's lease is active and matches this ID.
+func (client blobClient) SetTier(ctx context.Context, tier AccessTierType, snapshot *string, versionID *string, timeout *int32, rehydratePriority RehydratePriorityType, requestID *string, leaseID *string) (*BlobSetTierResponse, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.setTierPreparer(tier, snapshot, versionID, timeout, rehydratePriority, requestID, leaseID, ifTags)
+	req, err := client.setTierPreparer(tier, snapshot, versionID, timeout, rehydratePriority, requestID, leaseID)
 	if err != nil {
 		return nil, err
 	}
@@ -1984,7 +1972,7 @@ func (client blobClient) SetTier(ctx context.Context, tier AccessTierType, snaps
 }
 
 // setTierPreparer prepares the SetTier request.
-func (client blobClient) setTierPreparer(tier AccessTierType, snapshot *string, versionID *string, timeout *int32, rehydratePriority RehydratePriorityType, requestID *string, leaseID *string, ifTags *string) (pipeline.Request, error) {
+func (client blobClient) setTierPreparer(tier AccessTierType, snapshot *string, versionID *string, timeout *int32, rehydratePriority RehydratePriorityType, requestID *string, leaseID *string) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("PUT", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -2012,9 +2000,6 @@ func (client blobClient) setTierPreparer(tier AccessTierType, snapshot *string,
 	if leaseID != nil {
 		req.Header.Set("x-ms-lease-id", *leaseID)
 	}
-	if ifTags != nil {
-		req.Header.Set("x-ms-if-tags", *ifTags)
-	}
 	return req, nil
 }
 
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_block_blob.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_block_blob.go
index d350440ac..0008273ab 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_block_blob.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_block_blob.go
@@ -275,188 +275,6 @@ func (client blockBlobClient) getBlockListResponder(resp pipeline.Response) (pip
 	return result, nil
 }
 
-// PutBlobFromURL the Put Blob from URL operation creates a new Block Blob where the contents of the blob are read from
-// a given URL.  This API is supported beginning with the 2020-04-08 version. Partial updates are not supported with
-// Put Blob from URL; the content of an existing blob is overwritten with the content of the new blob.  To perform
-// partial updates to a block blob’s contents using a source URL, use the Put Block from URL API in conjunction with
-// Put Block List.
-//
-// contentLength is the length of the request. copySource is specifies the name of the source page blob snapshot. This
-// value is a URL of up to 2 KB in length that specifies a page blob snapshot. The value should be URL-encoded as it
-// would appear in a request URI. The source blob must either be public or must be authenticated via a shared access
-// signature. timeout is the timeout parameter is expressed in seconds. For more information, see <a
-// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
-// Timeouts for Blob Service Operations.</a> transactionalContentMD5 is specify the transactional md5 for the body, to
-// be validated by the service. blobContentType is optional. Sets the blob's content type. If specified, this property
-// is stored with the blob and returned with a read request. blobContentEncoding is optional. Sets the blob's content
-// encoding. If specified, this property is stored with the blob and returned with a read request. blobContentLanguage
-// is optional. Set the blob's content language. If specified, this property is stored with the blob and returned with
-// a read request. blobContentMD5 is optional. An MD5 hash of the blob content. Note that this hash is not validated,
-// as the hashes for the individual blocks were validated when each was uploaded. blobCacheControl is optional. Sets
-// the blob's cache control. If specified, this property is stored with the blob and returned with a read request.
-// metadata is optional. Specifies a user-defined name-value pair associated with the blob. If no name-value pairs are
-// specified, the operation will copy the metadata from the source blob or file to the destination blob. If one or more
-// name-value pairs are specified, the destination blob is created with the specified metadata, and metadata is not
-// copied from the source blob or file. Note that beginning with version 2009-09-19, metadata names must adhere to the
-// naming rules for C# identifiers. See Naming and Referencing Containers, Blobs, and Metadata for more information.
-// leaseID is if specified, the operation only succeeds if the resource's lease is active and matches this ID.
-// blobContentDisposition is optional. Sets the blob's Content-Disposition header. encryptionKey is optional. Specifies
-// the encryption key to use to encrypt the data provided in the request. If not specified, encryption is performed
-// with the root account encryption key.  For more information, see Encryption at Rest for Azure Storage Services.
-// encryptionKeySha256 is the SHA-256 hash of the provided encryption key. Must be provided if the x-ms-encryption-key
-// header is provided. encryptionAlgorithm is the algorithm used to produce the encryption key hash. Currently, the
-// only accepted value is "AES256". Must be provided if the x-ms-encryption-key header is provided. encryptionScope is
-// optional. Version 2019-07-07 and later.  Specifies the name of the encryption scope to use to encrypt the data
-// provided in the request. If not specified, encryption is performed with the default account encryption scope.  For
-// more information, see Encryption at Rest for Azure Storage Services. tier is optional. Indicates the tier to be set
-// on the blob. ifModifiedSince is specify this header value to operate only on a blob if it has been modified since
-// the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been
-// modified since the specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching
-// value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. ifTags is specify a
-// SQL where clause on blob tags to operate only on blobs with a matching value. sourceIfModifiedSince is specify this
-// header value to operate only on a blob if it has been modified since the specified date/time.
-// sourceIfUnmodifiedSince is specify this header value to operate only on a blob if it has not been modified since the
-// specified date/time. sourceIfMatch is specify an ETag value to operate only on blobs with a matching value.
-// sourceIfNoneMatch is specify an ETag value to operate only on blobs without a matching value. sourceIfTags is
-// specify a SQL where clause on blob tags to operate only on blobs with a matching value. requestID is provides a
-// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
-// analytics logging is enabled. sourceContentMD5 is specify the md5 calculated for the range of bytes that must be
-// read from the copy source. blobTagsString is optional.  Used to set blob tags in various blob operations.
-// copySourceBlobProperties is optional, default is true.  Indicates if properties from the source blob should be
-// copied.
-func (client blockBlobClient) PutBlobFromURL(ctx context.Context, contentLength int64, copySource string, timeout *int32, transactionalContentMD5 []byte, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, tier AccessTierType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, sourceIfTags *string, requestID *string, sourceContentMD5 []byte, blobTagsString *string, copySourceBlobProperties *bool) (*BlockBlobPutBlobFromURLResponse, error) {
-	if err := validate([]validation{
-		{targetValue: timeout,
-			constraints: []constraint{{target: "timeout", name: null, rule: false,
-				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
-		return nil, err
-	}
-	req, err := client.putBlobFromURLPreparer(contentLength, copySource, timeout, transactionalContentMD5, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseID, blobContentDisposition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, tier, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatch, sourceIfNoneMatch, sourceIfTags, requestID, sourceContentMD5, blobTagsString, copySourceBlobProperties)
-	if err != nil {
-		return nil, err
-	}
-	resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.putBlobFromURLResponder}, req)
-	if err != nil {
-		return nil, err
-	}
-	return resp.(*BlockBlobPutBlobFromURLResponse), err
-}
-
-// putBlobFromURLPreparer prepares the PutBlobFromURL request.
-func (client blockBlobClient) putBlobFromURLPreparer(contentLength int64, copySource string, timeout *int32, transactionalContentMD5 []byte, blobContentType *string, blobContentEncoding *string, blobContentLanguage *string, blobContentMD5 []byte, blobCacheControl *string, metadata map[string]string, leaseID *string, blobContentDisposition *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, tier AccessTierType, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, sourceIfModifiedSince *time.Time, sourceIfUnmodifiedSince *time.Time, sourceIfMatch *ETag, sourceIfNoneMatch *ETag, sourceIfTags *string, requestID *string, sourceContentMD5 []byte, blobTagsString *string, copySourceBlobProperties *bool) (pipeline.Request, error) {
-	req, err := pipeline.NewRequest("PUT", client.url, nil)
-	if err != nil {
-		return req, pipeline.NewError(err, "failed to create request")
-	}
-	params := req.URL.Query()
-	if timeout != nil {
-		params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
-	}
-	req.URL.RawQuery = params.Encode()
-	if transactionalContentMD5 != nil {
-		req.Header.Set("Content-MD5", base64.StdEncoding.EncodeToString(transactionalContentMD5))
-	}
-	req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
-	if blobContentType != nil {
-		req.Header.Set("x-ms-blob-content-type", *blobContentType)
-	}
-	if blobContentEncoding != nil {
-		req.Header.Set("x-ms-blob-content-encoding", *blobContentEncoding)
-	}
-	if blobContentLanguage != nil {
-		req.Header.Set("x-ms-blob-content-language", *blobContentLanguage)
-	}
-	if blobContentMD5 != nil {
-		req.Header.Set("x-ms-blob-content-md5", base64.StdEncoding.EncodeToString(blobContentMD5))
-	}
-	if blobCacheControl != nil {
-		req.Header.Set("x-ms-blob-cache-control", *blobCacheControl)
-	}
-	if metadata != nil {
-		for k, v := range metadata {
-			req.Header.Set("x-ms-meta-"+k, v)
-		}
-	}
-	if leaseID != nil {
-		req.Header.Set("x-ms-lease-id", *leaseID)
-	}
-	if blobContentDisposition != nil {
-		req.Header.Set("x-ms-blob-content-disposition", *blobContentDisposition)
-	}
-	if encryptionKey != nil {
-		req.Header.Set("x-ms-encryption-key", *encryptionKey)
-	}
-	if encryptionKeySha256 != nil {
-		req.Header.Set("x-ms-encryption-key-sha256", *encryptionKeySha256)
-	}
-	if encryptionAlgorithm != EncryptionAlgorithmNone {
-		req.Header.Set("x-ms-encryption-algorithm", string(encryptionAlgorithm))
-	}
-	if encryptionScope != nil {
-		req.Header.Set("x-ms-encryption-scope", *encryptionScope)
-	}
-	if tier != AccessTierNone {
-		req.Header.Set("x-ms-access-tier", string(tier))
-	}
-	if ifModifiedSince != nil {
-		req.Header.Set("If-Modified-Since", (*ifModifiedSince).In(gmt).Format(time.RFC1123))
-	}
-	if ifUnmodifiedSince != nil {
-		req.Header.Set("If-Unmodified-Since", (*ifUnmodifiedSince).In(gmt).Format(time.RFC1123))
-	}
-	if ifMatch != nil {
-		req.Header.Set("If-Match", string(*ifMatch))
-	}
-	if ifNoneMatch != nil {
-		req.Header.Set("If-None-Match", string(*ifNoneMatch))
-	}
-	if ifTags != nil {
-		req.Header.Set("x-ms-if-tags", *ifTags)
-	}
-	if sourceIfModifiedSince != nil {
-		req.Header.Set("x-ms-source-if-modified-since", (*sourceIfModifiedSince).In(gmt).Format(time.RFC1123))
-	}
-	if sourceIfUnmodifiedSince != nil {
-		req.Header.Set("x-ms-source-if-unmodified-since", (*sourceIfUnmodifiedSince).In(gmt).Format(time.RFC1123))
-	}
-	if sourceIfMatch != nil {
-		req.Header.Set("x-ms-source-if-match", string(*sourceIfMatch))
-	}
-	if sourceIfNoneMatch != nil {
-		req.Header.Set("x-ms-source-if-none-match", string(*sourceIfNoneMatch))
-	}
-	if sourceIfTags != nil {
-		req.Header.Set("x-ms-source-if-tags", *sourceIfTags)
-	}
-	req.Header.Set("x-ms-version", ServiceVersion)
-	if requestID != nil {
-		req.Header.Set("x-ms-client-request-id", *requestID)
-	}
-	if sourceContentMD5 != nil {
-		req.Header.Set("x-ms-source-content-md5", base64.StdEncoding.EncodeToString(sourceContentMD5))
-	}
-	if blobTagsString != nil {
-		req.Header.Set("x-ms-tags", *blobTagsString)
-	}
-	req.Header.Set("x-ms-copy-source", copySource)
-	if copySourceBlobProperties != nil {
-		req.Header.Set("x-ms-copy-source-blob-properties", strconv.FormatBool(*copySourceBlobProperties))
-	}
-	req.Header.Set("x-ms-blob-type", "BlockBlob")
-	return req, nil
-}
-
-// putBlobFromURLResponder handles the response to the PutBlobFromURL request.
-func (client blockBlobClient) putBlobFromURLResponder(resp pipeline.Response) (pipeline.Response, error) {
-	err := validateResponse(resp, http.StatusOK, http.StatusCreated)
-	if resp == nil {
-		return nil, err
-	}
-	io.Copy(ioutil.Discard, resp.Response().Body)
-	resp.Response().Body.Close()
-	return &BlockBlobPutBlobFromURLResponse{rawResponse: resp.Response()}, err
-}
-
 // StageBlock the Stage Block operation creates a new block to be committed as part of a blob
 //
 // blockID is a valid Base64 string value that identifies the block. Prior to encoding, the string must be less than or
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_client.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_client.go
index 24b9f1dd5..d697e37d7 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_client.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_client.go
@@ -10,7 +10,7 @@ import (
 
 const (
 	// ServiceVersion specifies the version of the operations used in this package.
-	ServiceVersion = "2020-04-08"
+	ServiceVersion = "2019-12-12"
 )
 
 // managementClient is the base client for Azblob.
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_container.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_container.go
index 2e2f176e5..88ff7df31 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_container.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_container.go
@@ -823,67 +823,6 @@ func (client containerClient) releaseLeaseResponder(resp pipeline.Response) (pip
 	return &ContainerReleaseLeaseResponse{rawResponse: resp.Response()}, err
 }
 
-// Rename renames an existing container.
-//
-// sourceContainerName is required.  Specifies the name of the container to rename. timeout is the timeout parameter is
-// expressed in seconds. For more information, see <a
-// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
-// Timeouts for Blob Service Operations.</a> requestID is provides a client-generated, opaque value with a 1 KB
-// character limit that is recorded in the analytics logs when storage analytics logging is enabled. sourceLeaseID is a
-// lease ID for the source path. If specified, the source path must have an active lease and the lease ID must match.
-func (client containerClient) Rename(ctx context.Context, sourceContainerName string, timeout *int32, requestID *string, sourceLeaseID *string) (*ContainerRenameResponse, error) {
-	if err := validate([]validation{
-		{targetValue: timeout,
-			constraints: []constraint{{target: "timeout", name: null, rule: false,
-				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
-		return nil, err
-	}
-	req, err := client.renamePreparer(sourceContainerName, timeout, requestID, sourceLeaseID)
-	if err != nil {
-		return nil, err
-	}
-	resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.renameResponder}, req)
-	if err != nil {
-		return nil, err
-	}
-	return resp.(*ContainerRenameResponse), err
-}
-
-// renamePreparer prepares the Rename request.
-func (client containerClient) renamePreparer(sourceContainerName string, timeout *int32, requestID *string, sourceLeaseID *string) (pipeline.Request, error) {
-	req, err := pipeline.NewRequest("PUT", client.url, nil)
-	if err != nil {
-		return req, pipeline.NewError(err, "failed to create request")
-	}
-	params := req.URL.Query()
-	if timeout != nil {
-		params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
-	}
-	params.Set("restype", "container")
-	params.Set("comp", "rename")
-	req.URL.RawQuery = params.Encode()
-	req.Header.Set("x-ms-version", ServiceVersion)
-	if requestID != nil {
-		req.Header.Set("x-ms-client-request-id", *requestID)
-	}
-	req.Header.Set("x-ms-source-container-name", sourceContainerName)
-	if sourceLeaseID != nil {
-		req.Header.Set("x-ms-source-lease-id", *sourceLeaseID)
-	}
-	return req, nil
-}
-
-// renameResponder handles the response to the Rename request.
-func (client containerClient) renameResponder(resp pipeline.Response) (pipeline.Response, error) {
-	err := validateResponse(resp, http.StatusOK)
-	if resp == nil {
-		return nil, err
-	}
-	io.Copy(ioutil.Discard, resp.Response().Body)
-	resp.Response().Body.Close()
-	return &ContainerRenameResponse{rawResponse: resp.Response()}, err
-}
-
 // RenewLease [Update] establishes and manages a lock on a container for delete operations. The lock duration can be 15
 // to 60 seconds, or can be infinite
 //
@@ -958,8 +897,8 @@ func (client containerClient) renewLeaseResponder(resp pipeline.Response) (pipel
 // href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
 // Timeouts for Blob Service Operations.</a> requestID is provides a client-generated, opaque value with a 1 KB
 // character limit that is recorded in the analytics logs when storage analytics logging is enabled.
-// deletedContainerName is optional.  Version 2019-12-12 and later.  Specifies the name of the deleted container to
-// restore. deletedContainerVersion is optional.  Version 2019-12-12 and later.  Specifies the version of the deleted
+// deletedContainerName is optional.  Version 2019-12-12 and laster.  Specifies the name of the deleted container to
+// restore. deletedContainerVersion is optional.  Version 2019-12-12 and laster.  Specifies the version of the deleted
 // container to restore.
 func (client containerClient) Restore(ctx context.Context, timeout *int32, requestID *string, deletedContainerName *string, deletedContainerVersion *string) (*ContainerRestoreResponse, error) {
 	if err := validate([]validation{
@@ -1170,63 +1109,3 @@ func (client containerClient) setMetadataResponder(resp pipeline.Response) (pipe
 	resp.Response().Body.Close()
 	return &ContainerSetMetadataResponse{rawResponse: resp.Response()}, err
 }
-
-// SubmitBatch the Batch operation allows multiple API calls to be embedded into a single HTTP request.
-//
-// body is initial data body will be closed upon successful return. Callers should ensure closure when receiving an
-// error.contentLength is the length of the request. multipartContentType is required. The value of this header must be
-// multipart/mixed with a batch boundary. Example header value: multipart/mixed; boundary=batch_<GUID> timeout is the
-// timeout parameter is expressed in seconds. For more information, see <a
-// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
-// Timeouts for Blob Service Operations.</a> requestID is provides a client-generated, opaque value with a 1 KB
-// character limit that is recorded in the analytics logs when storage analytics logging is enabled.
-func (client containerClient) SubmitBatch(ctx context.Context, body io.ReadSeeker, contentLength int64, multipartContentType string, timeout *int32, requestID *string) (*SubmitBatchResponse, error) {
-	if err := validate([]validation{
-		{targetValue: body,
-			constraints: []constraint{{target: "body", name: null, rule: true, chain: nil}}},
-		{targetValue: timeout,
-			constraints: []constraint{{target: "timeout", name: null, rule: false,
-				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
-		return nil, err
-	}
-	req, err := client.submitBatchPreparer(body, contentLength, multipartContentType, timeout, requestID)
-	if err != nil {
-		return nil, err
-	}
-	resp, err := client.Pipeline().Do(ctx, responderPolicyFactory{responder: client.submitBatchResponder}, req)
-	if err != nil {
-		return nil, err
-	}
-	return resp.(*SubmitBatchResponse), err
-}
-
-// submitBatchPreparer prepares the SubmitBatch request.
-func (client containerClient) submitBatchPreparer(body io.ReadSeeker, contentLength int64, multipartContentType string, timeout *int32, requestID *string) (pipeline.Request, error) {
-	req, err := pipeline.NewRequest("POST", client.url, body)
-	if err != nil {
-		return req, pipeline.NewError(err, "failed to create request")
-	}
-	params := req.URL.Query()
-	if timeout != nil {
-		params.Set("timeout", strconv.FormatInt(int64(*timeout), 10))
-	}
-	params.Set("restype", "container")
-	params.Set("comp", "batch")
-	req.URL.RawQuery = params.Encode()
-	req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
-	req.Header.Set("Content-Type", multipartContentType)
-	req.Header.Set("x-ms-version", ServiceVersion)
-	if requestID != nil {
-		req.Header.Set("x-ms-client-request-id", *requestID)
-	}
-	return req, nil
-}
-
-// submitBatchResponder handles the response to the SubmitBatch request.
-func (client containerClient) submitBatchResponder(resp pipeline.Response) (pipeline.Response, error) {
-	err := validateResponse(resp, http.StatusOK, http.StatusAccepted)
-	if resp == nil {
-		return nil, err
-	}
-	return &SubmitBatchResponse{rawResponse: resp.Response()}, err
-}
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_models.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_models.go
index d3a9084cc..78f467c40 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_models.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_models.go
@@ -174,21 +174,6 @@ func PossibleArchiveStatusTypeValues() []ArchiveStatusType {
 	return []ArchiveStatusType{ArchiveStatusNone, ArchiveStatusRehydratePendingToCool, ArchiveStatusRehydratePendingToHot}
 }
 
-// BlobDeleteType enumerates the values for blob delete type.
-type BlobDeleteType string
-
-const (
-	// BlobDeleteNone represents an empty BlobDeleteType.
-	BlobDeleteNone BlobDeleteType = ""
-	// BlobDeletePermanent ...
-	BlobDeletePermanent BlobDeleteType = "Permanent"
-)
-
-// PossibleBlobDeleteTypeValues returns an array of possible values for the BlobDeleteType const type.
-func PossibleBlobDeleteTypeValues() []BlobDeleteType {
-	return []BlobDeleteType{BlobDeleteNone, BlobDeletePermanent}
-}
-
 // BlobExpiryOptionsType enumerates the values for blob expiry options type.
 type BlobExpiryOptionsType string
 
@@ -494,8 +479,6 @@ func PossiblePublicAccessTypeValues() []PublicAccessType {
 type QueryFormatType string
 
 const (
-	// QueryFormatArrow ...
-	QueryFormatArrow QueryFormatType = "arrow"
 	// QueryFormatDelimited ...
 	QueryFormatDelimited QueryFormatType = "delimited"
 	// QueryFormatJSON ...
@@ -506,7 +489,7 @@ const (
 
 // PossibleQueryFormatTypeValues returns an array of possible values for the QueryFormatType const type.
 func PossibleQueryFormatTypeValues() []QueryFormatType {
-	return []QueryFormatType{QueryFormatArrow, QueryFormatDelimited, QueryFormatJSON, QueryFormatNone}
+	return []QueryFormatType{QueryFormatDelimited, QueryFormatJSON, QueryFormatNone}
 }
 
 // RehydratePriorityType enumerates the values for rehydrate priority type.
@@ -600,8 +583,6 @@ const (
 	StorageErrorCodeBlobArchived StorageErrorCodeType = "BlobArchived"
 	// StorageErrorCodeBlobBeingRehydrated ...
 	StorageErrorCodeBlobBeingRehydrated StorageErrorCodeType = "BlobBeingRehydrated"
-	// StorageErrorCodeBlobImmutableDueToPolicy ...
-	StorageErrorCodeBlobImmutableDueToPolicy StorageErrorCodeType = "BlobImmutableDueToPolicy"
 	// StorageErrorCodeBlobNotArchived ...
 	StorageErrorCodeBlobNotArchived StorageErrorCodeType = "BlobNotArchived"
 	// StorageErrorCodeBlobNotFound ...
@@ -802,7 +783,7 @@ const (
 
 // PossibleStorageErrorCodeTypeValues returns an array of possible values for the StorageErrorCodeType const type.
 func PossibleStorageErrorCodeTypeValues() []StorageErrorCodeType {
-	return []StorageErrorCodeType{StorageErrorCodeAccountAlreadyExists, StorageErrorCodeAccountBeingCreated, StorageErrorCodeAccountIsDisabled, StorageErrorCodeAppendPositionConditionNotMet, StorageErrorCodeAuthenticationFailed, StorageErrorCodeAuthorizationFailure, StorageErrorCodeAuthorizationPermissionMismatch, StorageErrorCodeAuthorizationProtocolMismatch, StorageErrorCodeAuthorizationResourceTypeMismatch, StorageErrorCodeAuthorizationServiceMismatch, StorageErrorCodeAuthorizationSourceIPMismatch, StorageErrorCodeBlobAlreadyExists, StorageErrorCodeBlobArchived, StorageErrorCodeBlobBeingRehydrated, StorageErrorCodeBlobImmutableDueToPolicy, StorageErrorCodeBlobNotArchived, StorageErrorCodeBlobNotFound, StorageErrorCodeBlobOverwritten, StorageErrorCodeBlobTierInadequateForContentLength, StorageErrorCodeBlockCountExceedsLimit, StorageErrorCodeBlockListTooLong, StorageErrorCodeCannotChangeToLowerTier, StorageErrorCodeCannotVerifyCopySource, StorageErrorCodeConditionHeadersNotSupported, StorageErrorCodeConditionNotMet, StorageErrorCodeContainerAlreadyExists, StorageErrorCodeContainerBeingDeleted, StorageErrorCodeContainerDisabled, StorageErrorCodeContainerNotFound, StorageErrorCodeContentLengthLargerThanTierLimit, StorageErrorCodeCopyAcrossAccountsNotSupported, StorageErrorCodeCopyIDMismatch, StorageErrorCodeEmptyMetadataKey, StorageErrorCodeFeatureVersionMismatch, StorageErrorCodeIncrementalCopyBlobMismatch, StorageErrorCodeIncrementalCopyOfEralierVersionSnapshotNotAllowed, StorageErrorCodeIncrementalCopySourceMustBeSnapshot, StorageErrorCodeInfiniteLeaseDurationRequired, StorageErrorCodeInsufficientAccountPermissions, StorageErrorCodeInternalError, StorageErrorCodeInvalidAuthenticationInfo, StorageErrorCodeInvalidBlobOrBlock, StorageErrorCodeInvalidBlobTier, StorageErrorCodeInvalidBlobType, StorageErrorCodeInvalidBlockID, StorageErrorCodeInvalidBlockList, StorageErrorCodeInvalidHeaderValue, StorageErrorCodeInvalidHTTPVerb, StorageErrorCodeInvalidInput, StorageErrorCodeInvalidMd5, StorageErrorCodeInvalidMetadata, StorageErrorCodeInvalidOperation, StorageErrorCodeInvalidPageRange, StorageErrorCodeInvalidQueryParameterValue, StorageErrorCodeInvalidRange, StorageErrorCodeInvalidResourceName, StorageErrorCodeInvalidSourceBlobType, StorageErrorCodeInvalidSourceBlobURL, StorageErrorCodeInvalidURI, StorageErrorCodeInvalidVersionForPageBlobOperation, StorageErrorCodeInvalidXMLDocument, StorageErrorCodeInvalidXMLNodeValue, StorageErrorCodeLeaseAlreadyBroken, StorageErrorCodeLeaseAlreadyPresent, StorageErrorCodeLeaseIDMismatchWithBlobOperation, StorageErrorCodeLeaseIDMismatchWithContainerOperation, StorageErrorCodeLeaseIDMismatchWithLeaseOperation, StorageErrorCodeLeaseIDMissing, StorageErrorCodeLeaseIsBreakingAndCannotBeAcquired, StorageErrorCodeLeaseIsBreakingAndCannotBeChanged, StorageErrorCodeLeaseIsBrokenAndCannotBeRenewed, StorageErrorCodeLeaseLost, StorageErrorCodeLeaseNotPresentWithBlobOperation, StorageErrorCodeLeaseNotPresentWithContainerOperation, StorageErrorCodeLeaseNotPresentWithLeaseOperation, StorageErrorCodeMaxBlobSizeConditionNotMet, StorageErrorCodeMd5Mismatch, StorageErrorCodeMetadataTooLarge, StorageErrorCodeMissingContentLengthHeader, StorageErrorCodeMissingRequiredHeader, StorageErrorCodeMissingRequiredQueryParameter, StorageErrorCodeMissingRequiredXMLNode, StorageErrorCodeMultipleConditionHeadersNotSupported, StorageErrorCodeNoAuthenticationInformation, StorageErrorCodeNone, StorageErrorCodeNoPendingCopyOperation, StorageErrorCodeOperationNotAllowedOnIncrementalCopyBlob, StorageErrorCodeOperationTimedOut, StorageErrorCodeOutOfRangeInput, StorageErrorCodeOutOfRangeQueryParameterValue, StorageErrorCodePendingCopyOperation, StorageErrorCodePreviousSnapshotCannotBeNewer, StorageErrorCodePreviousSnapshotNotFound, StorageErrorCodePreviousSnapshotOperationNotSupported, StorageErrorCodeRequestBodyTooLarge, StorageErrorCodeRequestURLFailedToParse, StorageErrorCodeResourceAlreadyExists, StorageErrorCodeResourceNotFound, StorageErrorCodeResourceTypeMismatch, StorageErrorCodeSequenceNumberConditionNotMet, StorageErrorCodeSequenceNumberIncrementTooLarge, StorageErrorCodeServerBusy, StorageErrorCodeSnaphotOperationRateExceeded, StorageErrorCodeSnapshotCountExceeded, StorageErrorCodeSnapshotsPresent, StorageErrorCodeSourceConditionNotMet, StorageErrorCodeSystemInUse, StorageErrorCodeTargetConditionNotMet, StorageErrorCodeUnauthorizedBlobOverwrite, StorageErrorCodeUnsupportedHeader, StorageErrorCodeUnsupportedHTTPVerb, StorageErrorCodeUnsupportedQueryParameter, StorageErrorCodeUnsupportedXMLNode}
+	return []StorageErrorCodeType{StorageErrorCodeAccountAlreadyExists, StorageErrorCodeAccountBeingCreated, StorageErrorCodeAccountIsDisabled, StorageErrorCodeAppendPositionConditionNotMet, StorageErrorCodeAuthenticationFailed, StorageErrorCodeAuthorizationFailure, StorageErrorCodeAuthorizationPermissionMismatch, StorageErrorCodeAuthorizationProtocolMismatch, StorageErrorCodeAuthorizationResourceTypeMismatch, StorageErrorCodeAuthorizationServiceMismatch, StorageErrorCodeAuthorizationSourceIPMismatch, StorageErrorCodeBlobAlreadyExists, StorageErrorCodeBlobArchived, StorageErrorCodeBlobBeingRehydrated, StorageErrorCodeBlobNotArchived, StorageErrorCodeBlobNotFound, StorageErrorCodeBlobOverwritten, StorageErrorCodeBlobTierInadequateForContentLength, StorageErrorCodeBlockCountExceedsLimit, StorageErrorCodeBlockListTooLong, StorageErrorCodeCannotChangeToLowerTier, StorageErrorCodeCannotVerifyCopySource, StorageErrorCodeConditionHeadersNotSupported, StorageErrorCodeConditionNotMet, StorageErrorCodeContainerAlreadyExists, StorageErrorCodeContainerBeingDeleted, StorageErrorCodeContainerDisabled, StorageErrorCodeContainerNotFound, StorageErrorCodeContentLengthLargerThanTierLimit, StorageErrorCodeCopyAcrossAccountsNotSupported, StorageErrorCodeCopyIDMismatch, StorageErrorCodeEmptyMetadataKey, StorageErrorCodeFeatureVersionMismatch, StorageErrorCodeIncrementalCopyBlobMismatch, StorageErrorCodeIncrementalCopyOfEralierVersionSnapshotNotAllowed, StorageErrorCodeIncrementalCopySourceMustBeSnapshot, StorageErrorCodeInfiniteLeaseDurationRequired, StorageErrorCodeInsufficientAccountPermissions, StorageErrorCodeInternalError, StorageErrorCodeInvalidAuthenticationInfo, StorageErrorCodeInvalidBlobOrBlock, StorageErrorCodeInvalidBlobTier, StorageErrorCodeInvalidBlobType, StorageErrorCodeInvalidBlockID, StorageErrorCodeInvalidBlockList, StorageErrorCodeInvalidHeaderValue, StorageErrorCodeInvalidHTTPVerb, StorageErrorCodeInvalidInput, StorageErrorCodeInvalidMd5, StorageErrorCodeInvalidMetadata, StorageErrorCodeInvalidOperation, StorageErrorCodeInvalidPageRange, StorageErrorCodeInvalidQueryParameterValue, StorageErrorCodeInvalidRange, StorageErrorCodeInvalidResourceName, StorageErrorCodeInvalidSourceBlobType, StorageErrorCodeInvalidSourceBlobURL, StorageErrorCodeInvalidURI, StorageErrorCodeInvalidVersionForPageBlobOperation, StorageErrorCodeInvalidXMLDocument, StorageErrorCodeInvalidXMLNodeValue, StorageErrorCodeLeaseAlreadyBroken, StorageErrorCodeLeaseAlreadyPresent, StorageErrorCodeLeaseIDMismatchWithBlobOperation, StorageErrorCodeLeaseIDMismatchWithContainerOperation, StorageErrorCodeLeaseIDMismatchWithLeaseOperation, StorageErrorCodeLeaseIDMissing, StorageErrorCodeLeaseIsBreakingAndCannotBeAcquired, StorageErrorCodeLeaseIsBreakingAndCannotBeChanged, StorageErrorCodeLeaseIsBrokenAndCannotBeRenewed, StorageErrorCodeLeaseLost, StorageErrorCodeLeaseNotPresentWithBlobOperation, StorageErrorCodeLeaseNotPresentWithContainerOperation, StorageErrorCodeLeaseNotPresentWithLeaseOperation, StorageErrorCodeMaxBlobSizeConditionNotMet, StorageErrorCodeMd5Mismatch, StorageErrorCodeMetadataTooLarge, StorageErrorCodeMissingContentLengthHeader, StorageErrorCodeMissingRequiredHeader, StorageErrorCodeMissingRequiredQueryParameter, StorageErrorCodeMissingRequiredXMLNode, StorageErrorCodeMultipleConditionHeadersNotSupported, StorageErrorCodeNoAuthenticationInformation, StorageErrorCodeNone, StorageErrorCodeNoPendingCopyOperation, StorageErrorCodeOperationNotAllowedOnIncrementalCopyBlob, StorageErrorCodeOperationTimedOut, StorageErrorCodeOutOfRangeInput, StorageErrorCodeOutOfRangeQueryParameterValue, StorageErrorCodePendingCopyOperation, StorageErrorCodePreviousSnapshotCannotBeNewer, StorageErrorCodePreviousSnapshotNotFound, StorageErrorCodePreviousSnapshotOperationNotSupported, StorageErrorCodeRequestBodyTooLarge, StorageErrorCodeRequestURLFailedToParse, StorageErrorCodeResourceAlreadyExists, StorageErrorCodeResourceNotFound, StorageErrorCodeResourceTypeMismatch, StorageErrorCodeSequenceNumberConditionNotMet, StorageErrorCodeSequenceNumberIncrementTooLarge, StorageErrorCodeServerBusy, StorageErrorCodeSnaphotOperationRateExceeded, StorageErrorCodeSnapshotCountExceeded, StorageErrorCodeSnapshotsPresent, StorageErrorCodeSourceConditionNotMet, StorageErrorCodeSystemInUse, StorageErrorCodeTargetConditionNotMet, StorageErrorCodeUnauthorizedBlobOverwrite, StorageErrorCodeUnsupportedHeader, StorageErrorCodeUnsupportedHTTPVerb, StorageErrorCodeUnsupportedQueryParameter, StorageErrorCodeUnsupportedXMLNode}
 }
 
 // SyncCopyStatusType enumerates the values for sync copy status type.
@@ -1277,21 +1258,6 @@ func (absr AppendBlobSealResponse) Version() string {
 	return absr.rawResponse.Header.Get("x-ms-version")
 }
 
-// ArrowConfiguration - arrow configuration
-type ArrowConfiguration struct {
-	Schema []ArrowField `xml:"Schema>Field"`
-}
-
-// ArrowField - field of an arrow schema
-type ArrowField struct {
-	// XMLName is used for marshalling and is subject to removal in a future release.
-	XMLName   xml.Name `xml:"Field"`
-	Type      string   `xml:"Type"`
-	Name      *string  `xml:"Name"`
-	Precision *int32   `xml:"Precision"`
-	Scale     *int32   `xml:"Scale"`
-}
-
 // BlobAbortCopyFromURLResponse ...
 type BlobAbortCopyFromURLResponse struct {
 	rawResponse *http.Response
@@ -2263,19 +2229,6 @@ func (bgpr BlobGetPropertiesResponse) IsServerEncrypted() string {
 	return bgpr.rawResponse.Header.Get("x-ms-server-encrypted")
 }
 
-// LastAccessed returns the value for header x-ms-last-access-time.
-func (bgpr BlobGetPropertiesResponse) LastAccessed() time.Time {
-	s := bgpr.rawResponse.Header.Get("x-ms-last-access-time")
-	if s == "" {
-		return time.Time{}
-	}
-	t, err := time.Parse(time.RFC1123, s)
-	if err != nil {
-		t = time.Time{}
-	}
-	return t
-}
-
 // LastModified returns the value for header Last-Modified.
 func (bgpr BlobGetPropertiesResponse) LastModified() time.Time {
 	s := bgpr.rawResponse.Header.Get("Last-Modified")
@@ -2358,13 +2311,15 @@ type BlobHierarchyListSegment struct {
 // BlobItemInternal - An Azure Storage blob
 type BlobItemInternal struct {
 	// XMLName is used for marshalling and is subject to removal in a future release.
-	XMLName                   xml.Name          `xml:"Blob"`
-	Name                      string            `xml:"Name"`
-	Deleted                   bool              `xml:"Deleted"`
-	Snapshot                  string            `xml:"Snapshot"`
-	VersionID                 *string           `xml:"VersionId"`
-	IsCurrentVersion          *bool             `xml:"IsCurrentVersion"`
-	Properties                BlobProperties    `xml:"Properties"`
+	XMLName          xml.Name       `xml:"Blob"`
+	Name             string         `xml:"Name"`
+	Deleted          bool           `xml:"Deleted"`
+	Snapshot         string         `xml:"Snapshot"`
+	VersionID        *string        `xml:"VersionId"`
+	IsCurrentVersion *bool          `xml:"IsCurrentVersion"`
+	Properties       BlobProperties `xml:"Properties"`
+
+	// TODO funky generator type -> *BlobMetadata
 	Metadata                  Metadata          `xml:"Metadata"`
 	BlobTags                  *BlobTags         `xml:"Tags"`
 	ObjectReplicationMetadata map[string]string `xml:"ObjectReplicationMetadata"`
@@ -2431,19 +2386,18 @@ type BlobProperties struct {
 	AccessTierChangeTime *time.Time `xml:"AccessTierChangeTime"`
 	TagCount             *int32     `xml:"TagCount"`
 	ExpiresOn            *time.Time `xml:"Expiry-Time"`
-	IsSealed             *bool      `xml:"Sealed"`
+	IsSealed             *bool      `xml:"IsSealed"`
 	// RehydratePriority - Possible values include: 'RehydratePriorityHigh', 'RehydratePriorityStandard', 'RehydratePriorityNone'
 	RehydratePriority RehydratePriorityType `xml:"RehydratePriority"`
-	LastAccessedOn    *time.Time            `xml:"LastAccessTime"`
 }
 
-// MarshalXML implements the xml.Marshaler interface for BlobPropertiesInternal.
+// MarshalXML implements the xml.Marshaler interface for BlobProperties.
 func (bpi BlobProperties) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
 	bpi2 := (*blobProperties)(unsafe.Pointer(&bpi))
 	return e.EncodeElement(*bpi2, start)
 }
 
-// UnmarshalXML implements the xml.Unmarshaler interface for BlobPropertiesInternal.
+// UnmarshalXML implements the xml.Unmarshaler interface for BlobProperties.
 func (bpi *BlobProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
 	bpi2 := (*blobProperties)(unsafe.Pointer(bpi))
 	return d.DecodeElement(bpi2, &start)
@@ -3288,7 +3242,7 @@ type Block struct {
 	// Name - The base64 encoded block ID.
 	Name string `xml:"Name"`
 	// Size - The block size in bytes.
-	Size int32 `xml:"Size"`
+	Size int64 `xml:"Size"`
 }
 
 // BlockBlobCommitBlockListResponse ...
@@ -3408,110 +3362,6 @@ func (bbcblr BlockBlobCommitBlockListResponse) XMsContentCrc64() []byte {
 	return b
 }
 
-// BlockBlobPutBlobFromURLResponse ...
-type BlockBlobPutBlobFromURLResponse struct {
-	rawResponse *http.Response
-}
-
-// Response returns the raw HTTP response object.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) Response() *http.Response {
-	return bbpbfur.rawResponse
-}
-
-// StatusCode returns the HTTP status code of the response, e.g. 200.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) StatusCode() int {
-	return bbpbfur.rawResponse.StatusCode
-}
-
-// Status returns the HTTP status message of the response, e.g. "200 OK".
-func (bbpbfur BlockBlobPutBlobFromURLResponse) Status() string {
-	return bbpbfur.rawResponse.Status
-}
-
-// ClientRequestID returns the value for header x-ms-client-request-id.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) ClientRequestID() string {
-	return bbpbfur.rawResponse.Header.Get("x-ms-client-request-id")
-}
-
-// ContentMD5 returns the value for header Content-MD5.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) ContentMD5() []byte {
-	s := bbpbfur.rawResponse.Header.Get("Content-MD5")
-	if s == "" {
-		return nil
-	}
-	b, err := base64.StdEncoding.DecodeString(s)
-	if err != nil {
-		b = nil
-	}
-	return b
-}
-
-// Date returns the value for header Date.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) Date() time.Time {
-	s := bbpbfur.rawResponse.Header.Get("Date")
-	if s == "" {
-		return time.Time{}
-	}
-	t, err := time.Parse(time.RFC1123, s)
-	if err != nil {
-		t = time.Time{}
-	}
-	return t
-}
-
-// EncryptionKeySha256 returns the value for header x-ms-encryption-key-sha256.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) EncryptionKeySha256() string {
-	return bbpbfur.rawResponse.Header.Get("x-ms-encryption-key-sha256")
-}
-
-// EncryptionScope returns the value for header x-ms-encryption-scope.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) EncryptionScope() string {
-	return bbpbfur.rawResponse.Header.Get("x-ms-encryption-scope")
-}
-
-// ErrorCode returns the value for header x-ms-error-code.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) ErrorCode() string {
-	return bbpbfur.rawResponse.Header.Get("x-ms-error-code")
-}
-
-// ETag returns the value for header ETag.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) ETag() ETag {
-	return ETag(bbpbfur.rawResponse.Header.Get("ETag"))
-}
-
-// IsServerEncrypted returns the value for header x-ms-request-server-encrypted.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) IsServerEncrypted() string {
-	return bbpbfur.rawResponse.Header.Get("x-ms-request-server-encrypted")
-}
-
-// LastModified returns the value for header Last-Modified.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) LastModified() time.Time {
-	s := bbpbfur.rawResponse.Header.Get("Last-Modified")
-	if s == "" {
-		return time.Time{}
-	}
-	t, err := time.Parse(time.RFC1123, s)
-	if err != nil {
-		t = time.Time{}
-	}
-	return t
-}
-
-// RequestID returns the value for header x-ms-request-id.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) RequestID() string {
-	return bbpbfur.rawResponse.Header.Get("x-ms-request-id")
-}
-
-// Version returns the value for header x-ms-version.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) Version() string {
-	return bbpbfur.rawResponse.Header.Get("x-ms-version")
-}
-
-// VersionID returns the value for header x-ms-version-id.
-func (bbpbfur BlockBlobPutBlobFromURLResponse) VersionID() string {
-	return bbpbfur.rawResponse.Header.Get("x-ms-version-id")
-}
-
 // BlockBlobStageBlockFromURLResponse ...
 type BlockBlobStageBlockFromURLResponse struct {
 	rawResponse *http.Response
@@ -4571,59 +4421,6 @@ func (crlr ContainerReleaseLeaseResponse) Version() string {
 	return crlr.rawResponse.Header.Get("x-ms-version")
 }
 
-// ContainerRenameResponse ...
-type ContainerRenameResponse struct {
-	rawResponse *http.Response
-}
-
-// Response returns the raw HTTP response object.
-func (crr ContainerRenameResponse) Response() *http.Response {
-	return crr.rawResponse
-}
-
-// StatusCode returns the HTTP status code of the response, e.g. 200.
-func (crr ContainerRenameResponse) StatusCode() int {
-	return crr.rawResponse.StatusCode
-}
-
-// Status returns the HTTP status message of the response, e.g. "200 OK".
-func (crr ContainerRenameResponse) Status() string {
-	return crr.rawResponse.Status
-}
-
-// ClientRequestID returns the value for header x-ms-client-request-id.
-func (crr ContainerRenameResponse) ClientRequestID() string {
-	return crr.rawResponse.Header.Get("x-ms-client-request-id")
-}
-
-// Date returns the value for header Date.
-func (crr ContainerRenameResponse) Date() time.Time {
-	s := crr.rawResponse.Header.Get("Date")
-	if s == "" {
-		return time.Time{}
-	}
-	t, err := time.Parse(time.RFC1123, s)
-	if err != nil {
-		t = time.Time{}
-	}
-	return t
-}
-
-// ErrorCode returns the value for header x-ms-error-code.
-func (crr ContainerRenameResponse) ErrorCode() string {
-	return crr.rawResponse.Header.Get("x-ms-error-code")
-}
-
-// RequestID returns the value for header x-ms-request-id.
-func (crr ContainerRenameResponse) RequestID() string {
-	return crr.rawResponse.Header.Get("x-ms-request-id")
-}
-
-// Version returns the value for header x-ms-version.
-func (crr ContainerRenameResponse) Version() string {
-	return crr.rawResponse.Header.Get("x-ms-version")
-}
-
 // ContainerRenewLeaseResponse ...
 type ContainerRenewLeaseResponse struct {
 	rawResponse *http.Response
@@ -5542,11 +5339,6 @@ func (dr downloadResponse) ETag() ETag {
 	return ETag(dr.rawResponse.Header.Get("ETag"))
 }
 
-// IsCurrentVersion returns the value for header x-ms-is-current-version.
-func (dr downloadResponse) IsCurrentVersion() string {
-	return dr.rawResponse.Header.Get("x-ms-is-current-version")
-}
-
 // IsSealed returns the value for header x-ms-blob-sealed.
 func (dr downloadResponse) IsSealed() string {
 	return dr.rawResponse.Header.Get("x-ms-blob-sealed")
@@ -5557,19 +5349,6 @@ func (dr downloadResponse) IsServerEncrypted() string {
 	return dr.rawResponse.Header.Get("x-ms-server-encrypted")
 }
 
-// LastAccessed returns the value for header x-ms-last-access-time.
-func (dr downloadResponse) LastAccessed() time.Time {
-	s := dr.rawResponse.Header.Get("x-ms-last-access-time")
-	if s == "" {
-		return time.Time{}
-	}
-	t, err := time.Parse(time.RFC1123, s)
-	if err != nil {
-		t = time.Time{}
-	}
-	return t
-}
-
 // LastModified returns the value for header Last-Modified.
 func (dr downloadResponse) LastModified() time.Time {
 	s := dr.rawResponse.Header.Get("Last-Modified")
@@ -5639,10 +5418,10 @@ func (dr downloadResponse) VersionID() string {
 // FilterBlobItem - Blob info from a Filter Blobs API call
 type FilterBlobItem struct {
 	// XMLName is used for marshalling and is subject to removal in a future release.
-	XMLName       xml.Name  `xml:"Blob"`
-	Name          string    `xml:"Name"`
-	ContainerName string    `xml:"ContainerName"`
-	Tags          *BlobTags `xml:"Tags"`
+	XMLName       xml.Name `xml:"Blob"`
+	Name          string   `xml:"Name"`
+	ContainerName string   `xml:"ContainerName"`
+	TagValue      string   `xml:"TagValue"`
 }
 
 // FilterBlobSegment - The result of a Filter Blobs API call
@@ -6749,11 +6528,10 @@ type PageRange struct {
 
 // QueryFormat ...
 type QueryFormat struct {
-	// Type - Possible values include: 'QueryFormatDelimited', 'QueryFormatJSON', 'QueryFormatArrow', 'QueryFormatNone'
+	// Type - Possible values include: 'QueryFormatDelimited', 'QueryFormatJSON', 'QueryFormatNone'
 	Type                       QueryFormatType             `xml:"Type"`
 	DelimitedTextConfiguration *DelimitedTextConfiguration `xml:"DelimitedTextConfiguration"`
 	JSONTextConfiguration      *JSONTextConfiguration      `xml:"JsonTextConfiguration"`
-	ArrowConfiguration         *ArrowConfiguration         `xml:"ArrowConfiguration"`
 }
 
 // QueryRequest - the quick query body
@@ -7052,8 +6830,6 @@ type RetentionPolicy struct {
 	Enabled bool `xml:"Enabled"`
 	// Days - Indicates the number of days that metrics or logging or soft-deleted data should be retained. All data older than this value will be deleted
 	Days *int32 `xml:"Days"`
-	// AllowPermanentDelete - Indicates whether permanent delete is allowed on this storage account.
-	AllowPermanentDelete *bool `xml:"AllowPermanentDelete"`
 }
 
 // ServiceGetAccountInfoResponse ...
@@ -7104,11 +6880,6 @@ func (sgair ServiceGetAccountInfoResponse) ErrorCode() string {
 	return sgair.rawResponse.Header.Get("x-ms-error-code")
 }
 
-// IsHierarchicalNamespaceEnabled returns the value for header x-ms-is-hns-enabled.
-func (sgair ServiceGetAccountInfoResponse) IsHierarchicalNamespaceEnabled() string {
-	return sgair.rawResponse.Header.Get("x-ms-is-hns-enabled")
-}
-
 // RequestID returns the value for header x-ms-request-id.
 func (sgair ServiceGetAccountInfoResponse) RequestID() string {
 	return sgair.rawResponse.Header.Get("x-ms-request-id")
@@ -7260,11 +7031,6 @@ type StaticWebsite struct {
 	DefaultIndexDocumentPath *string `xml:"DefaultIndexDocumentPath"`
 }
 
-// StorageError ...
-// type StorageError struct {
-// 	Message *string `xml:"Message"`
-// }
-
 // StorageServiceProperties - Storage Service Properties.
 type StorageServiceProperties struct {
 	rawResponse   *http.Response
@@ -7368,7 +7134,7 @@ func (sss StorageServiceStats) Version() string {
 	return sss.rawResponse.Header.Get("x-ms-version")
 }
 
-// SubmitBatchResponse - Wraps the response from the containerClient.SubmitBatch method.
+// SubmitBatchResponse - Wraps the response from the serviceClient.SubmitBatch method.
 type SubmitBatchResponse struct {
 	rawResponse *http.Response
 }
@@ -7500,7 +7266,7 @@ func init() {
 		validateError(errors.New("size mismatch between AccessPolicy and accessPolicy"))
 	}
 	if reflect.TypeOf((*BlobProperties)(nil)).Elem().Size() != reflect.TypeOf((*blobProperties)(nil)).Elem().Size() {
-		validateError(errors.New("size mismatch between BlobPropertiesInternal and blobPropertiesInternal"))
+		validateError(errors.New("size mismatch between BlobProperties and blobProperties"))
 	}
 	if reflect.TypeOf((*ContainerProperties)(nil)).Elem().Size() != reflect.TypeOf((*containerProperties)(nil)).Elem().Size() {
 		validateError(errors.New("size mismatch between ContainerProperties and containerProperties"))
@@ -7511,7 +7277,7 @@ func init() {
 }
 
 const (
-	rfc3339Format = "2006-01-02T15:04:05Z"
+	rfc3339Format = "2006-01-02T15:04:05Z" //This was wrong in the generated code, FYI
 )
 
 // used to convert times from UTC to GMT before sending across the wire
@@ -7626,9 +7392,8 @@ type blobProperties struct {
 	AccessTierChangeTime      *timeRFC1123          `xml:"AccessTierChangeTime"`
 	TagCount                  *int32                `xml:"TagCount"`
 	ExpiresOn                 *timeRFC1123          `xml:"Expiry-Time"`
-	IsSealed                  *bool                 `xml:"Sealed"`
+	IsSealed                  *bool                 `xml:"IsSealed"`
 	RehydratePriority         RehydratePriorityType `xml:"RehydratePriority"`
-	LastAccessedOn            *timeRFC1123          `xml:"LastAccessTime"`
 }
 
 // internal type used for marshalling
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_page_blob.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_page_blob.go
index 6bc10f099..b55ae12bb 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_page_blob.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_page_blob.go
@@ -48,17 +48,16 @@ func newPageBlobClient(url url.URL, p pipeline.Pipeline) pageBlobClient {
 // on a blob if it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to
 // operate only on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value
 // to operate only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs
-// without a matching value. ifTags is specify a SQL where clause on blob tags to operate only on blobs with a matching
-// value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the
-// analytics logs when storage analytics logging is enabled.
-func (client pageBlobClient) ClearPages(ctx context.Context, contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (*PageBlobClearPagesResponse, error) {
+// without a matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is
+// recorded in the analytics logs when storage analytics logging is enabled.
+func (client pageBlobClient) ClearPages(ctx context.Context, contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobClearPagesResponse, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.clearPagesPreparer(contentLength, timeout, rangeParameter, leaseID, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, requestID)
+	req, err := client.clearPagesPreparer(contentLength, timeout, rangeParameter, leaseID, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
 	if err != nil {
 		return nil, err
 	}
@@ -70,7 +69,7 @@ func (client pageBlobClient) ClearPages(ctx context.Context, contentLength int64
 }
 
 // clearPagesPreparer prepares the ClearPages request.
-func (client pageBlobClient) clearPagesPreparer(contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (pipeline.Request, error) {
+func (client pageBlobClient) clearPagesPreparer(contentLength int64, timeout *int32, rangeParameter *string, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, ifSequenceNumberLessThanOrEqualTo *int64, ifSequenceNumberLessThan *int64, ifSequenceNumberEqualTo *int64, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("PUT", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -121,9 +120,6 @@ func (client pageBlobClient) clearPagesPreparer(contentLength int64, timeout *in
 	if ifNoneMatch != nil {
 		req.Header.Set("If-None-Match", string(*ifNoneMatch))
 	}
-	if ifTags != nil {
-		req.Header.Set("x-ms-if-tags", *ifTags)
-	}
 	req.Header.Set("x-ms-version", ServiceVersion)
 	if requestID != nil {
 		req.Header.Set("x-ms-client-request-id", *requestID)
@@ -157,17 +153,16 @@ func (client pageBlobClient) clearPagesResponder(resp pipeline.Response) (pipeli
 // it has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only
 // on a blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate
 // only on blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a
-// matching value. ifTags is specify a SQL where clause on blob tags to operate only on blobs with a matching value.
-// requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics
-// logs when storage analytics logging is enabled.
-func (client pageBlobClient) CopyIncremental(ctx context.Context, copySource string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (*PageBlobCopyIncrementalResponse, error) {
+// matching value. requestID is provides a client-generated, opaque value with a 1 KB character limit that is recorded
+// in the analytics logs when storage analytics logging is enabled.
+func (client pageBlobClient) CopyIncremental(ctx context.Context, copySource string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobCopyIncrementalResponse, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.copyIncrementalPreparer(copySource, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, requestID)
+	req, err := client.copyIncrementalPreparer(copySource, timeout, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
 	if err != nil {
 		return nil, err
 	}
@@ -179,7 +174,7 @@ func (client pageBlobClient) CopyIncremental(ctx context.Context, copySource str
 }
 
 // copyIncrementalPreparer prepares the CopyIncremental request.
-func (client pageBlobClient) copyIncrementalPreparer(copySource string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (pipeline.Request, error) {
+func (client pageBlobClient) copyIncrementalPreparer(copySource string, timeout *int32, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("PUT", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -202,9 +197,6 @@ func (client pageBlobClient) copyIncrementalPreparer(copySource string, timeout
 	if ifNoneMatch != nil {
 		req.Header.Set("If-None-Match", string(*ifNoneMatch))
 	}
-	if ifTags != nil {
-		req.Header.Set("x-ms-if-tags", *ifTags)
-	}
 	req.Header.Set("x-ms-copy-source", copySource)
 	req.Header.Set("x-ms-version", ServiceVersion)
 	if requestID != nil {
@@ -609,18 +601,17 @@ func (client pageBlobClient) getPageRangesDiffResponder(resp pipeline.Response)
 // Services. ifModifiedSince is specify this header value to operate only on a blob if it has been modified since the
 // specified date/time. ifUnmodifiedSince is specify this header value to operate only on a blob if it has not been
 // modified since the specified date/time. ifMatch is specify an ETag value to operate only on blobs with a matching
-// value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. ifTags is specify a
-// SQL where clause on blob tags to operate only on blobs with a matching value. requestID is provides a
-// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
+// value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value. requestID is provides
+// a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
 // analytics logging is enabled.
-func (client pageBlobClient) Resize(ctx context.Context, blobContentLength int64, timeout *int32, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (*PageBlobResizeResponse, error) {
+func (client pageBlobClient) Resize(ctx context.Context, blobContentLength int64, timeout *int32, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (*PageBlobResizeResponse, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.resizePreparer(blobContentLength, timeout, leaseID, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, requestID)
+	req, err := client.resizePreparer(blobContentLength, timeout, leaseID, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, requestID)
 	if err != nil {
 		return nil, err
 	}
@@ -632,7 +623,7 @@ func (client pageBlobClient) Resize(ctx context.Context, blobContentLength int64
 }
 
 // resizePreparer prepares the Resize request.
-func (client pageBlobClient) resizePreparer(blobContentLength int64, timeout *int32, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, requestID *string) (pipeline.Request, error) {
+func (client pageBlobClient) resizePreparer(blobContentLength int64, timeout *int32, leaseID *string, encryptionKey *string, encryptionKeySha256 *string, encryptionAlgorithm EncryptionAlgorithmType, encryptionScope *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, requestID *string) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("PUT", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -670,9 +661,6 @@ func (client pageBlobClient) resizePreparer(blobContentLength int64, timeout *in
 	if ifNoneMatch != nil {
 		req.Header.Set("If-None-Match", string(*ifNoneMatch))
 	}
-	if ifTags != nil {
-		req.Header.Set("x-ms-if-tags", *ifTags)
-	}
 	req.Header.Set("x-ms-blob-content-length", strconv.FormatInt(blobContentLength, 10))
 	req.Header.Set("x-ms-version", ServiceVersion)
 	if requestID != nil {
@@ -703,18 +691,18 @@ func (client pageBlobClient) resizeResponder(resp pipeline.Response) (pipeline.R
 // has been modified since the specified date/time. ifUnmodifiedSince is specify this header value to operate only on a
 // blob if it has not been modified since the specified date/time. ifMatch is specify an ETag value to operate only on
 // blobs with a matching value. ifNoneMatch is specify an ETag value to operate only on blobs without a matching value.
-// ifTags is specify a SQL where clause on blob tags to operate only on blobs with a matching value. blobSequenceNumber
-// is set for page blobs only. The sequence number is a user-controlled value that you can use to track requests. The
-// value of the sequence number must be between 0 and 2^63 - 1. requestID is provides a client-generated, opaque value
-// with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled.
-func (client pageBlobClient) UpdateSequenceNumber(ctx context.Context, sequenceNumberAction SequenceNumberActionType, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, blobSequenceNumber *int64, requestID *string) (*PageBlobUpdateSequenceNumberResponse, error) {
+// blobSequenceNumber is set for page blobs only. The sequence number is a user-controlled value that you can use to
+// track requests. The value of the sequence number must be between 0 and 2^63 - 1. requestID is provides a
+// client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage
+// analytics logging is enabled.
+func (client pageBlobClient) UpdateSequenceNumber(ctx context.Context, sequenceNumberAction SequenceNumberActionType, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, blobSequenceNumber *int64, requestID *string) (*PageBlobUpdateSequenceNumberResponse, error) {
 	if err := validate([]validation{
 		{targetValue: timeout,
 			constraints: []constraint{{target: "timeout", name: null, rule: false,
 				chain: []constraint{{target: "timeout", name: inclusiveMinimum, rule: 0, chain: nil}}}}}}); err != nil {
 		return nil, err
 	}
-	req, err := client.updateSequenceNumberPreparer(sequenceNumberAction, timeout, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, blobSequenceNumber, requestID)
+	req, err := client.updateSequenceNumberPreparer(sequenceNumberAction, timeout, leaseID, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, blobSequenceNumber, requestID)
 	if err != nil {
 		return nil, err
 	}
@@ -726,7 +714,7 @@ func (client pageBlobClient) UpdateSequenceNumber(ctx context.Context, sequenceN
 }
 
 // updateSequenceNumberPreparer prepares the UpdateSequenceNumber request.
-func (client pageBlobClient) updateSequenceNumberPreparer(sequenceNumberAction SequenceNumberActionType, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, ifTags *string, blobSequenceNumber *int64, requestID *string) (pipeline.Request, error) {
+func (client pageBlobClient) updateSequenceNumberPreparer(sequenceNumberAction SequenceNumberActionType, timeout *int32, leaseID *string, ifModifiedSince *time.Time, ifUnmodifiedSince *time.Time, ifMatch *ETag, ifNoneMatch *ETag, blobSequenceNumber *int64, requestID *string) (pipeline.Request, error) {
 	req, err := pipeline.NewRequest("PUT", client.url, nil)
 	if err != nil {
 		return req, pipeline.NewError(err, "failed to create request")
@@ -752,9 +740,6 @@ func (client pageBlobClient) updateSequenceNumberPreparer(sequenceNumberAction S
 	if ifNoneMatch != nil {
 		req.Header.Set("If-None-Match", string(*ifNoneMatch))
 	}
-	if ifTags != nil {
-		req.Header.Set("x-ms-if-tags", *ifTags)
-	}
 	req.Header.Set("x-ms-sequence-number-action", string(sequenceNumberAction))
 	if blobSequenceNumber != nil {
 		req.Header.Set("x-ms-blob-sequence-number", strconv.FormatInt(*blobSequenceNumber, 10))
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_version.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_version.go
index ee8e4d5e0..200b2f568 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_version.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_generated_version.go
@@ -5,7 +5,7 @@ package azblob
 
 // UserAgent returns the UserAgent string to use when sending http.Requests.
 func UserAgent() string {
-	return "Azure-SDK-For-Go/0.0.0 azblob/2020-04-08"
+	return "Azure-SDK-For-Go/0.0.0 azblob/2019-12-12"
 }
 
 // Version returns the semantic version (see http://semver.org) of the client.
diff --git a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_response_helpers.go b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_response_helpers.go
index d586b7d42..5c086c5cf 100644
--- a/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_response_helpers.go
+++ b/vendor/github.com/Azure/azure-storage-blob-go/azblob/zz_response_helpers.go
@@ -45,7 +45,7 @@ func (dr downloadResponse) NewHTTPHeaders() BlobHTTPHeaders {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// downloadResponse wraps AutoRest generated downloadResponse and helps to provide info for retry.
+// DownloadResponse wraps AutoRest generated DownloadResponse and helps to provide info for retry.
 type DownloadResponse struct {
 	r       *downloadResponse
 	ctx     context.Context
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 09e8986bc..c0fb1f87b 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -35,7 +35,7 @@ github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources
 github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-05-01/resources
 github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage
 github.com/Azure/azure-sdk-for-go/version
-# github.com/Azure/azure-storage-blob-go v0.14.0
+# github.com/Azure/azure-storage-blob-go v0.13.0
 ## explicit
 github.com/Azure/azure-storage-blob-go/azblob
 # github.com/Azure/go-autorest v14.2.0+incompatible
@@ -646,6 +646,8 @@ google.golang.org/protobuf/types/known/emptypb
 google.golang.org/protobuf/types/known/fieldmaskpb
 google.golang.org/protobuf/types/known/timestamppb
 google.golang.org/protobuf/types/known/wrapperspb
+# gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
+## explicit
 # gopkg.in/ini.v1 v1.66.4
 ## explicit
 gopkg.in/ini.v1


Test patch:
diff --git a/internal/dnfjson/dnfjson_test.go b/internal/dnfjson/dnfjson_test.go
index 700b3c411..f10867120 100644
--- a/internal/dnfjson/dnfjson_test.go
+++ b/internal/dnfjson/dnfjson_test.go
@@ -483,3 +483,68 @@ func expectedResult(repo rpmmd.RepoConfig) []rpmmd.PackageSpec {
 	}
 	return exp
 }
+
+func TestErrorRepoInfo(t *testing.T) {
+	if !*forceDNF {
+		// dnf tests aren't forced: skip them if the dnf sniff check fails
+		if !dnfInstalled() {
+			t.Skip()
+		}
+	}
+
+	assert := assert.New(t)
+
+	type testCase struct {
+		repo   rpmmd.RepoConfig
+		expMsg string
+	}
+
+	testCases := []testCase{
+		{
+			repo: rpmmd.RepoConfig{
+				Name:     "",
+				BaseURL:  "https://0.0.0.0/baseos/repo",
+				Metalink: "https://0.0.0.0/baseos/metalink",
+			},
+			expMsg: "[https://0.0.0.0/baseos/repo]",
+		},
+		{
+			repo: rpmmd.RepoConfig{
+				Name:     "baseos",
+				BaseURL:  "https://0.0.0.0/baseos/repo",
+				Metalink: "https://0.0.0.0/baseos/metalink",
+			},
+			expMsg: "[baseos: https://0.0.0.0/baseos/repo]",
+		},
+		{
+			repo: rpmmd.RepoConfig{
+				Name:     "fedora",
+				Metalink: "https://0.0.0.0/f35/metalink",
+			},
+			expMsg: "[fedora: https://0.0.0.0/f35/metalink]",
+		},
+		{
+			repo: rpmmd.RepoConfig{
+				Name:       "",
+				MirrorList: "https://0.0.0.0/baseos/mirrors",
+			},
+			expMsg: "[https://0.0.0.0/baseos/mirrors]",
+		},
+	}
+
+	solver := NewSolver("f36", "36", "x86_64", "/tmp/cache")
+	solver.SetDNFJSONPath("../../dnf-json")
+	for idx, tc := range testCases {
+		t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
+			_, err := solver.Depsolve([]rpmmd.PackageSet{
+				{
+					Include:      []string{"osbuild"},
+					Exclude:      nil,
+					Repositories: []rpmmd.RepoConfig{tc.repo},
+				},
+			})
+			assert.Error(err)
+			assert.Contains(err.Error(), tc.expMsg)
+		})
+	}
+}
diff --git a/internal/osbuild1/stage_test.go b/internal/osbuild1/stage_test.go
deleted file mode 100644
index b2545548b..000000000
--- a/internal/osbuild1/stage_test.go
+++ /dev/null
@@ -1,337 +0,0 @@
-package osbuild1
-
-import (
-	"bytes"
-	"encoding/json"
-	"reflect"
-	"testing"
-
-	"github.com/google/uuid"
-)
-
-func TestStage_UnmarshalJSON(t *testing.T) {
-	nullUUID := uuid.MustParse("00000000-0000-0000-0000-000000000000")
-	type fields struct {
-		Name    string
-		Options StageOptions
-	}
-	type args struct {
-		data []byte
-	}
-	tests := []struct {
-		name    string
-		fields  fields
-		args    args
-		wantErr bool
-	}{
-		{
-			name: "invalid json",
-			args: args{
-				data: []byte(`{"name":"org.osbuild.foo","options":{"bar":null}`),
-			},
-			wantErr: true,
-		},
-		{
-			name: "unknown stage",
-			args: args{
-				data: []byte(`{"name":"org.osbuild.foo","options":{"bar":null}}`),
-			},
-			wantErr: true,
-		},
-		{
-			name: "missing options",
-			args: args{
-				data: []byte(`{"name":"org.osbuild.locale"}`),
-			},
-			wantErr: true,
-		},
-		{
-			name: "missing name",
-			args: args{
-				data: []byte(`{"foo":null,"options":{"bar":null}}`),
-			},
-			wantErr: true,
-		},
-		{
-			name: "chrony",
-			fields: fields{
-				Name:    "org.osbuild.chrony",
-				Options: &ChronyStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.chrony","options":{"timeservers":null}}`),
-			},
-		},
-		{
-			name: "firewall",
-			fields: fields{
-				Name:    "org.osbuild.firewall",
-				Options: &FirewallStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.firewall","options":{}}`),
-			},
-		},
-		{
-			name: "fix-bls",
-			fields: fields{
-				Name:    "org.osbuild.fix-bls",
-				Options: &FixBLSStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.fix-bls","options":{}}`),
-			},
-		},
-		{
-			name: "fstab",
-			fields: fields{
-				Name:    "org.osbuild.fstab",
-				Options: &FSTabStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.fstab","options":{"filesystems":null}}`),
-			},
-		},
-		{
-			name: "groups",
-			fields: fields{
-				Name:    "org.osbuild.groups",
-				Options: &GroupsStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.groups","options":{"groups":null}}`),
-			},
-		},
-		{
-			name: "grub2",
-			fields: fields{
-				Name: "org.osbuild.grub2",
-				Options: &GRUB2StageOptions{
-					RootFilesystemUUID: nullUUID,
-				},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000"}}`),
-			},
-		},
-		{
-			name: "grub2-uefi",
-			fields: fields{
-				Name: "org.osbuild.grub2",
-				Options: &GRUB2StageOptions{
-					RootFilesystemUUID: nullUUID,
-					UEFI: &GRUB2UEFI{
-						Vendor: "vendor",
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000","uefi":{"vendor":"vendor"}}}`),
-			},
-		},
-		{
-			name: "grub2-separate-boot",
-			fields: fields{
-				Name: "org.osbuild.grub2",
-				Options: &GRUB2StageOptions{
-					RootFilesystemUUID: nullUUID,
-					BootFilesystemUUID: &nullUUID,
-				},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000","boot_fs_uuid":"00000000-0000-0000-0000-000000000000"}}`),
-			},
-		},
-		{
-			name: "hostname",
-			fields: fields{
-				Name:    "org.osbuild.hostname",
-				Options: &HostnameStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.hostname","options":{"hostname":""}}`),
-			},
-		},
-		{
-			name: "keymap",
-			fields: fields{
-				Name:    "org.osbuild.keymap",
-				Options: &KeymapStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.keymap","options":{"keymap":""}}`),
-			},
-		},
-		{
-			name: "locale",
-			fields: fields{
-				Name:    "org.osbuild.locale",
-				Options: &LocaleStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.locale","options":{"language":""}}`),
-			},
-		},
-		{
-			name: "rhsm-empty",
-			fields: fields{
-				Name:    "org.osbuild.rhsm",
-				Options: &RHSMStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.rhsm","options":{}}`),
-			},
-		},
-		{
-			name: "rhsm",
-			fields: fields{
-				Name: "org.osbuild.rhsm",
-				Options: &RHSMStageOptions{
-					DnfPlugins: &RHSMStageOptionsDnfPlugins{
-						ProductID: &RHSMStageOptionsDnfPlugin{
-							Enabled: false,
-						},
-						SubscriptionManager: &RHSMStageOptionsDnfPlugin{
-							Enabled: false,
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.rhsm","options":{"dnf-plugins":{"product-id":{"enabled":false},"subscription-manager":{"enabled":false}}}}`),
-			},
-		},
-		{
-			name: "rpm-empty",
-			fields: fields{
-				Name:    "org.osbuild.rpm",
-				Options: &RPMStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.rpm","options":{"packages":null}}`),
-			},
-		},
-		{
-			name: "rpm",
-			fields: fields{
-				Name: "org.osbuild.rpm",
-				Options: &RPMStageOptions{
-					GPGKeys: []string{"key1", "key2"},
-					Packages: []RPMPackage{
-						{
-							Checksum: "checksum1",
-						},
-						{
-							Checksum: "checksum2",
-							CheckGPG: true,
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.rpm","options":{"gpgkeys":["key1","key2"],"packages":[{"checksum":"checksum1"},{"checksum":"checksum2","check_gpg":true}]}}`),
-			},
-		},
-		{
-			name: "rpm-ostree",
-			fields: fields{
-				Name: "org.osbuild.rpm-ostree",
-				Options: &RPMOSTreeStageOptions{
-					EtcGroupMembers: []string{
-						"wheel",
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.rpm-ostree","options":{"etc_group_members":["wheel"]}}`),
-			},
-		},
-		{
-			name: "script",
-			fields: fields{
-				Name:    "org.osbuild.script",
-				Options: &ScriptStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.script","options":{"script":""}}`),
-			},
-		},
-		{
-			name: "selinux",
-			fields: fields{
-				Name:    "org.osbuild.selinux",
-				Options: &SELinuxStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.selinux","options":{"file_contexts":""}}`),
-			},
-		},
-		{
-			name: "systemd",
-			fields: fields{
-				Name:    "org.osbuild.systemd",
-				Options: &SystemdStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.systemd","options":{}}`),
-			},
-		},
-		{
-			name: "systemd-enabled",
-			fields: fields{
-				Name: "org.osbuild.systemd",
-				Options: &SystemdStageOptions{
-					EnabledServices: []string{"foo.service"},
-				},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.systemd","options":{"enabled_services":["foo.service"]}}`),
-			},
-		},
-		{
-			name: "timezone",
-			fields: fields{
-				Name:    "org.osbuild.timezone",
-				Options: &TimezoneStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.timezone","options":{"zone":""}}`),
-			},
-		},
-		{
-			name: "users",
-			fields: fields{
-				Name:    "org.osbuild.users",
-				Options: &UsersStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"name":"org.osbuild.users","options":{"users":null}}`),
-			},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			stage := &Stage{
-				Name:    tt.fields.Name,
-				Options: tt.fields.Options,
-			}
-			var gotStage Stage
-			if err := gotStage.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
-				t.Errorf("Stage.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
-			}
-			if tt.wantErr {
-				return
-			}
-			gotBytes, err := json.Marshal(stage)
-			if err != nil {
-				t.Errorf("Could not marshal stage: %v", err)
-			}
-			if !bytes.Equal(gotBytes, tt.args.data) {
-				t.Errorf("Expected `%v`, got `%v`", string(tt.args.data), string(gotBytes))
-			}
-			if !reflect.DeepEqual(&gotStage, stage) {
-				t.Errorf("got {%v, %v}, expected {%v, %v}", gotStage.Name, gotStage.Options, stage.Name, stage.Options)
-			}
-		})
-	}
-}
diff --git a/internal/osbuild2/stage_test.go b/internal/osbuild2/stage_test.go
deleted file mode 100644
index bd16549cf..000000000
--- a/internal/osbuild2/stage_test.go
+++ /dev/null
@@ -1,898 +0,0 @@
-package osbuild2
-
-import (
-	"bytes"
-	"encoding/json"
-	"reflect"
-	"testing"
-
-	"github.com/google/uuid"
-	"github.com/osbuild/osbuild-composer/internal/common"
-)
-
-func TestStage_UnmarshalJSON(t *testing.T) {
-	nullUUID := uuid.MustParse("00000000-0000-0000-0000-000000000000")
-	type fields struct {
-		Type    string
-		Options StageOptions
-	}
-	type args struct {
-		data []byte
-	}
-	tests := []struct {
-		name    string
-		fields  fields
-		args    args
-		wantErr bool
-	}{
-		{
-			name: "invalid json",
-			args: args{
-				data: []byte(`{"type":"org.osbuild.foo","options":{"bar":null}}`),
-			},
-			wantErr: true,
-		},
-		{
-			name: "unknown stage",
-			args: args{
-				data: []byte(`{"type":"org.osbuild.foo","options":{"bar":null}}`),
-			},
-			wantErr: true,
-		},
-		{
-			name: "missing options",
-			args: args{
-				data: []byte(`{"type":"org.osbuild.locale"}`),
-			},
-			wantErr: true,
-		},
-		{
-			name: "missing name",
-			args: args{
-				data: []byte(`{"foo":null,"options":{"bar":null}}`),
-			},
-			wantErr: true,
-		},
-		{
-			name: "authselect",
-			fields: fields{
-				Type: "org.osbuild.authselect",
-				Options: &AuthselectStageOptions{
-					Profile: "sssd",
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.authselect","options":{"profile":"sssd"}}`),
-			},
-		},
-		{
-			name: "authselect-features",
-			fields: fields{
-				Type: "org.osbuild.authselect",
-				Options: &AuthselectStageOptions{
-					Profile:  "nis",
-					Features: []string{"with-ecryptfs", "with-mkhomedir"},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.authselect","options":{"profile":"nis","features":["with-ecryptfs","with-mkhomedir"]}}`),
-			},
-		},
-		{
-			name: "cloud-init",
-			fields: fields{
-				Type: "org.osbuild.cloud-init",
-				Options: &CloudInitStageOptions{
-					Filename: "00-default_user.cfg",
-					Config: CloudInitConfigFile{
-						SystemInfo: &CloudInitConfigSystemInfo{
-							DefaultUser: &CloudInitConfigDefaultUser{
-								Name: "ec2-user",
-							},
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.cloud-init","options":{"filename":"00-default_user.cfg","config":{"system_info":{"default_user":{"name":"ec2-user"}}}}}`),
-			},
-		},
-		{
-			name: "chrony-timeservers",
-			fields: fields{
-				Type: "org.osbuild.chrony",
-				Options: &ChronyStageOptions{
-					Timeservers: []string{
-						"ntp1.example.com",
-						"ntp2.example.com",
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.chrony","options":{"timeservers":["ntp1.example.com","ntp2.example.com"]}}`),
-			},
-		},
-		{
-			name: "chrony-servers",
-			fields: fields{
-				Type: "org.osbuild.chrony",
-				Options: &ChronyStageOptions{
-					Servers: []ChronyConfigServer{
-						{
-							Hostname: "127.0.0.1",
-							Minpoll:  common.IntToPtr(0),
-							Maxpoll:  common.IntToPtr(4),
-							Iburst:   common.BoolToPtr(true),
-							Prefer:   common.BoolToPtr(false),
-						},
-						{
-							Hostname: "ntp.example.com",
-						},
-					},
-					LeapsecTz: common.StringToPtr(""),
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.chrony","options":{"servers":[{"hostname":"127.0.0.1","minpoll":0,"maxpoll":4,"iburst":true,"prefer":false},{"hostname":"ntp.example.com"}],"leapsectz":""}}`),
-			},
-		},
-		{
-			name: "dnf-config",
-			fields: fields{
-				Type:    "org.osbuild.dnf.config",
-				Options: &DNFConfigStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.dnf.config","options":{}}`),
-			},
-		},
-		{
-			name: "dnf-automatic-config",
-			fields: fields{
-				Type:    "org.osbuild.dnf-automatic.config",
-				Options: &DNFAutomaticConfigStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.dnf-automatic.config","options":{}}`),
-			},
-		},
-		{
-			name: "dracut",
-			fields: fields{
-				Type:    "org.osbuild.dracut",
-				Options: &DracutStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.dracut","options":{"kernel":null}}`),
-			},
-		},
-		{
-			name: "dracut.conf",
-			fields: fields{
-				Type: "org.osbuild.dracut.conf",
-				Options: &DracutConfStageOptions{
-					Filename: "testing.conf",
-					Config: DracutConfigFile{
-						Compress:       "xz",
-						AddModules:     []string{"floppy"},
-						OmitModules:    []string{"nouveau"},
-						AddDrivers:     []string{"driver1"},
-						ForceDrivers:   []string{"driver2"},
-						Filesystems:    []string{"ext4"},
-						Install:        []string{"file1"},
-						EarlyMicrocode: common.BoolToPtr(false),
-						Reproducible:   common.BoolToPtr(false),
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.dracut.conf","options":{"filename":"testing.conf","config":{"compress":"xz","add_dracutmodules":["floppy"],"omit_dracutmodules":["nouveau"],"add_drivers":["driver1"],"force_drivers":["driver2"],"filesystems":["ext4"],"install_items":["file1"],"early_microcode":false,"reproducible":false}}}`),
-			},
-		},
-		{
-			name: "firewall",
-			fields: fields{
-				Type:    "org.osbuild.firewall",
-				Options: &FirewallStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.firewall","options":{}}`),
-			},
-		},
-		{
-			name: "fix-bls",
-			fields: fields{
-				Type:    "org.osbuild.fix-bls",
-				Options: &FixBLSStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.fix-bls","options":{}}`),
-			},
-		},
-		{
-			name: "fix-bls-empty-prefix",
-			fields: fields{
-				Type: "org.osbuild.fix-bls",
-				Options: &FixBLSStageOptions{
-					Prefix: common.StringToPtr(""),
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.fix-bls","options":{"prefix":""}}`),
-			},
-		},
-		{
-			name: "fstab",
-			fields: fields{
-				Type:    "org.osbuild.fstab",
-				Options: &FSTabStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.fstab","options":{"filesystems":null}}`),
-			},
-		},
-		{
-			name: "groups",
-			fields: fields{
-				Type:    "org.osbuild.groups",
-				Options: &GroupsStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.groups","options":{"groups":null}}`),
-			},
-		},
-		{
-			name: "grub2",
-			fields: fields{
-				Type: "org.osbuild.grub2",
-				Options: &GRUB2StageOptions{
-					RootFilesystemUUID: nullUUID,
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000"}}`),
-			},
-		},
-		{
-			name: "grub2-uefi",
-			fields: fields{
-				Type: "org.osbuild.grub2",
-				Options: &GRUB2StageOptions{
-					RootFilesystemUUID: nullUUID,
-					UEFI: &GRUB2UEFI{
-						Vendor: "vendor",
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000","uefi":{"vendor":"vendor"}}}`),
-			},
-		},
-		{
-			name: "grub2-separate-boot",
-			fields: fields{
-				Type: "org.osbuild.grub2",
-				Options: &GRUB2StageOptions{
-					RootFilesystemUUID: nullUUID,
-					BootFilesystemUUID: &nullUUID,
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.grub2","options":{"root_fs_uuid":"00000000-0000-0000-0000-000000000000","boot_fs_uuid":"00000000-0000-0000-0000-000000000000"}}`),
-			},
-		},
-		{
-			name: "hostname",
-			fields: fields{
-				Type:    "org.osbuild.hostname",
-				Options: &HostnameStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.hostname","options":{"hostname":""}}`),
-			},
-		},
-		{
-			name: "keymap",
-			fields: fields{
-				Type:    "org.osbuild.keymap",
-				Options: &KeymapStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.keymap","options":{"keymap":""}}`),
-			},
-		},
-		{
-			name: "keymap-x11-keymap",
-			fields: fields{
-				Type: "org.osbuild.keymap",
-				Options: &KeymapStageOptions{
-					Keymap: "us",
-					X11Keymap: &X11KeymapOptions{
-						Layouts: []string{"cz"},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.keymap","options":{"keymap":"us","x11-keymap":{"layouts":["cz"]}}}`),
-			},
-		},
-		{
-			name: "modprobe",
-			fields: fields{
-				Type: "org.osbuild.modprobe",
-				Options: &ModprobeStageOptions{
-					Filename: "disallow-modules.conf",
-					Commands: ModprobeConfigCmdList{
-						&ModprobeConfigCmdBlacklist{
-							Command:    "blacklist",
-							Modulename: "nouveau",
-						},
-						&ModprobeConfigCmdBlacklist{
-							Command:    "blacklist",
-							Modulename: "floppy",
-						},
-						&ModprobeConfigCmdInstall{
-							Command:    "install",
-							Modulename: "nf_conntrack",
-							Cmdline:    "/usr/sbin/modprobe --ignore-install nf_conntrack $CMDLINE_OPTS && /usr/sbin/sysctl --quiet --pattern 'net[.]netfilter[.]nf_conntrack.*' --system",
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.modprobe","options":{"filename":"disallow-modules.conf","commands":[{"command":"blacklist","modulename":"nouveau"},{"command":"blacklist","modulename":"floppy"},{"command":"install","modulename":"nf_conntrack","cmdline":"/usr/sbin/modprobe --ignore-install nf_conntrack $CMDLINE_OPTS \u0026\u0026 /usr/sbin/sysctl --quiet --pattern 'net[.]netfilter[.]nf_conntrack.*' --system"}]}}`),
-			},
-		},
-		{
-			name: "locale",
-			fields: fields{
-				Type:    "org.osbuild.locale",
-				Options: &LocaleStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.locale","options":{"language":""}}`),
-			},
-		},
-		{
-			name: "pam-limits-conf-str",
-			fields: fields{
-				Type: "org.osbuild.pam.limits.conf",
-				Options: &PamLimitsConfStageOptions{
-					Filename: "example.conf",
-					Config: []PamLimitsConfigLine{
-						{
-							Domain: "user1",
-							Type:   PamLimitsTypeHard,
-							Item:   PamLimitsItemNofile,
-							Value:  PamLimitsValueUnlimited,
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.pam.limits.conf","options":{"filename":"example.conf","config":[{"domain":"user1","type":"hard","item":"nofile","value":"unlimited"}]}}`),
-			},
-		},
-		{
-			name: "pam-limits-conf-int",
-			fields: fields{
-				Type: "org.osbuild.pam.limits.conf",
-				Options: &PamLimitsConfStageOptions{
-					Filename: "example.conf",
-					Config: []PamLimitsConfigLine{
-						{
-							Domain: "user1",
-							Type:   PamLimitsTypeHard,
-							Item:   PamLimitsItemNofile,
-							Value:  PamLimitsValueInt(-1),
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.pam.limits.conf","options":{"filename":"example.conf","config":[{"domain":"user1","type":"hard","item":"nofile","value":-1}]}}`),
-			},
-		},
-		{
-			name: "tuned",
-			fields: fields{
-				Type: "org.osbuild.tuned",
-				Options: &TunedStageOptions{
-					Profiles: []string{"sap-hana"},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.tuned","options":{"profiles":["sap-hana"]}}`),
-			},
-		},
-		{
-			name: "rhsm-empty",
-			fields: fields{
-				Type:    "org.osbuild.rhsm",
-				Options: &RHSMStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.rhsm","options":{}}`),
-			},
-		},
-		{
-			name: "rhsm",
-			fields: fields{
-				Type: "org.osbuild.rhsm",
-				Options: &RHSMStageOptions{
-					DnfPlugins: &RHSMStageOptionsDnfPlugins{
-						ProductID: &RHSMStageOptionsDnfPlugin{
-							Enabled: false,
-						},
-						SubscriptionManager: &RHSMStageOptionsDnfPlugin{
-							Enabled: false,
-						},
-					},
-					SubMan: &RHSMStageOptionsSubMan{
-						Rhsm: &SubManConfigRHSMSection{
-							ManageRepos: common.BoolToPtr(false),
-						},
-						Rhsmcertd: &SubManConfigRHSMCERTDSection{
-							AutoRegistration: common.BoolToPtr(true),
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.rhsm","options":{"dnf-plugins":{"product-id":{"enabled":false},"subscription-manager":{"enabled":false}},"subscription-manager":{"rhsm":{"manage_repos":false},"rhsmcertd":{"auto_registration":true}}}}`),
-			},
-		},
-		{
-			name: "script",
-			fields: fields{
-				Type:    "org.osbuild.script",
-				Options: &ScriptStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.script","options":{"script":""}}`),
-			},
-		},
-		{
-			name: "selinux",
-			fields: fields{
-				Type:    "org.osbuild.selinux",
-				Options: &SELinuxStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.selinux","options":{"file_contexts":""}}`),
-			},
-		},
-		{
-			name: "selinux-force_autorelabel",
-			fields: fields{
-				Type: "org.osbuild.selinux",
-				Options: &SELinuxStageOptions{
-					ForceAutorelabel: common.BoolToPtr(true),
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.selinux","options":{"file_contexts":"","force_autorelabel":true}}`),
-			},
-		},
-		{
-			name: "selinux.config-empty",
-			fields: fields{
-				Type:    "org.osbuild.selinux.config",
-				Options: &SELinuxConfigStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.selinux.config","options":{}}`),
-			},
-		},
-		{
-			name: "selinux.config",
-			fields: fields{
-				Type: "org.osbuild.selinux.config",
-				Options: &SELinuxConfigStageOptions{
-					State: SELinuxStatePermissive,
-					Type:  SELinuxTypeMinimum,
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.selinux.config","options":{"state":"permissive","type":"minimum"}}`),
-			},
-		},
-		{
-			name: "sysconfig",
-			fields: fields{
-				Type:    "org.osbuild.sysconfig",
-				Options: &SysconfigStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.sysconfig","options":{}}`),
-			},
-		},
-		{
-			name: "sysconfig-data",
-			fields: fields{
-				Type: "org.osbuild.sysconfig",
-				Options: &SysconfigStageOptions{
-					Kernel: &SysconfigKernelOptions{
-						UpdateDefault: true,
-						DefaultKernel: "kernel",
-					},
-					Network: &SysconfigNetworkOptions{
-						Networking: true,
-						NoZeroConf: true,
-					},
-					NetworkScripts: &NetworkScriptsOptions{
-						IfcfgFiles: map[string]IfcfgFile{
-							"eth0": {
-								Device:    "eth0",
-								Bootproto: IfcfgBootprotoDHCP,
-								OnBoot:    common.BoolToPtr(true),
-								Type:      IfcfgTypeEthernet,
-								UserCtl:   common.BoolToPtr(true),
-								PeerDNS:   common.BoolToPtr(true),
-								IPv6Init:  common.BoolToPtr(false),
-							},
-							"eth1": {
-								Device:    "eth1",
-								Bootproto: IfcfgBootprotoDHCP,
-								OnBoot:    common.BoolToPtr(true),
-								Type:      IfcfgTypeEthernet,
-								UserCtl:   common.BoolToPtr(false),
-								PeerDNS:   common.BoolToPtr(true),
-								IPv6Init:  common.BoolToPtr(true),
-							},
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.sysconfig","options":{"kernel":{"update_default":true,"default_kernel":"kernel"},"network":{"networking":true,"no_zero_conf":true},"network-scripts":{"ifcfg":{"eth0":{"bootproto":"dhcp","device":"eth0","ipv6init":false,"onboot":true,"peerdns":true,"type":"Ethernet","userctl":true},"eth1":{"bootproto":"dhcp","device":"eth1","ipv6init":true,"onboot":true,"peerdns":true,"type":"Ethernet","userctl":false}}}}}`),
-			},
-		},
-		{
-			name: "sysctld",
-			fields: fields{
-				Type: "org.osbuild.sysctld",
-				Options: &SysctldStageOptions{
-					Filename: "example.conf",
-					Config: []SysctldConfigLine{
-						{
-							Key:   "net.ipv4.conf.*.rp_filter",
-							Value: "2",
-						},
-						{
-							Key: "-net.ipv4.conf.all.rp_filter",
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.sysctld","options":{"filename":"example.conf","config":[{"key":"net.ipv4.conf.*.rp_filter","value":"2"},{"key":"-net.ipv4.conf.all.rp_filter"}]}}`),
-			},
-		},
-		{
-			name: "systemd",
-			fields: fields{
-				Type:    "org.osbuild.systemd",
-				Options: &SystemdStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.systemd","options":{}}`),
-			},
-		},
-		{
-			name: "systemd-enabled",
-			fields: fields{
-				Type: "org.osbuild.systemd",
-				Options: &SystemdStageOptions{
-					EnabledServices: []string{"foo.service"},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.systemd","options":{"enabled_services":["foo.service"]}}`),
-			},
-		},
-		{
-			name: "systemd-unit-dropins",
-			fields: fields{
-				Type: "org.osbuild.systemd.unit",
-				Options: &SystemdUnitStageOptions{
-					Unit:   "nm-cloud-setup.service",
-					Dropin: "10-rh-enable-for-ec2.conf",
-					Config: SystemdServiceUnitDropin{
-						Service: &SystemdUnitServiceSection{
-							Environment: "NM_CLOUD_SETUP_EC2=yes",
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.systemd.unit","options":{"unit":"nm-cloud-setup.service","dropin":"10-rh-enable-for-ec2.conf","config":{"Service":{"Environment":"NM_CLOUD_SETUP_EC2=yes"}}}}`),
-			},
-		},
-		{
-			name: "systemd-logind",
-			fields: fields{
-				Type: "org.osbuild.systemd-logind",
-				Options: &SystemdLogindStageOptions{
-					Filename: "10-ec2-getty-fix.conf",
-					Config: SystemdLogindConfigDropin{
-						Login: SystemdLogindConfigLoginSection{
-							NAutoVTs: common.IntToPtr(0),
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.systemd-logind","options":{"filename":"10-ec2-getty-fix.conf","config":{"Login":{"NAutoVTs":0}}}}`),
-			},
-		},
-		{
-			name: "timezone",
-			fields: fields{
-				Type:    "org.osbuild.timezone",
-				Options: &TimezoneStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.timezone","options":{"zone":""}}`),
-			},
-		},
-		{
-			name: "tmpfilesd",
-			fields: fields{
-				Type: "org.osbuild.tmpfilesd",
-				Options: &TmpfilesdStageOptions{
-					Filename: "example.conf",
-					Config: []TmpfilesdConfigLine{
-						{
-							Type: "d",
-							Path: "/tmp/my-example-path",
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.tmpfilesd","options":{"filename":"example.conf","config":[{"type":"d","path":"/tmp/my-example-path"}]}}`),
-			},
-		},
-		{
-			name: "users",
-			fields: fields{
-				Type:    "org.osbuild.users",
-				Options: &UsersStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.users","options":{"users":null}}`),
-			},
-		},
-		{
-			name: "sshd.config",
-			fields: fields{
-				Type:    "org.osbuild.sshd.config",
-				Options: &SshdConfigStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.sshd.config","options":{"config":{}}}`),
-			},
-		},
-		{
-			name: "sshd.config-data1",
-			fields: fields{
-				Type: "org.osbuild.sshd.config",
-				Options: &SshdConfigStageOptions{
-					Config: SshdConfigConfig{
-						PasswordAuthentication:          common.BoolToPtr(false),
-						ChallengeResponseAuthentication: common.BoolToPtr(false),
-						ClientAliveInterval:             common.IntToPtr(42),
-						PermitRootLogin:                 PermitRootLoginValueNo,
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.sshd.config","options":{"config":{"PasswordAuthentication":false,"ChallengeResponseAuthentication":false,"ClientAliveInterval":42,"PermitRootLogin":false}}}`),
-			},
-		},
-		{
-			name: "sshd.config-data2",
-			fields: fields{
-				Type: "org.osbuild.sshd.config",
-				Options: &SshdConfigStageOptions{
-					Config: SshdConfigConfig{
-						PasswordAuthentication:          common.BoolToPtr(false),
-						ChallengeResponseAuthentication: common.BoolToPtr(false),
-						ClientAliveInterval:             common.IntToPtr(42),
-						PermitRootLogin:                 PermitRootLoginValueForcedCommandsOnly,
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.sshd.config","options":{"config":{"PasswordAuthentication":false,"ChallengeResponseAuthentication":false,"ClientAliveInterval":42,"PermitRootLogin":"forced-commands-only"}}}`),
-			},
-		},
-		{
-			name: "authconfig",
-			fields: fields{
-				Type:    "org.osbuild.authconfig",
-				Options: &AuthconfigStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.authconfig","options":{}}`),
-			},
-		},
-		{
-			name: "pwquality.conf",
-			fields: fields{
-				Type:    "org.osbuild.pwquality.conf",
-				Options: &PwqualityConfStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.pwquality.conf","options":{"config":{}}}`),
-			},
-		},
-		{
-			name: "yum.config",
-			fields: fields{
-				Type:    "org.osbuild.yum.config",
-				Options: &YumConfigStageOptions{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.yum.config","options":{}}`),
-			},
-		},
-		{
-			name: "yum.repos",
-			fields: fields{
-				Type: "org.osbuild.yum.repos",
-				Options: &YumReposStageOptions{
-					Filename: "test.repo",
-					Repos: []YumRepository{
-						{
-							Id:      "my-repo",
-							BaseURL: []string{"http://example.org/repo"},
-						},
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.yum.repos","options":{"filename":"test.repo","repos":[{"id":"my-repo","baseurl":["http://example.org/repo"]}]}}`),
-			},
-		},
-	}
-	for idx, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			stage := &Stage{
-				Type:    tt.fields.Type,
-				Options: tt.fields.Options,
-			}
-			var gotStage Stage
-			if err := gotStage.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
-				t.Errorf("Stage.UnmarshalJSON() error = %v, wantErr %v [idx: %d]", err, tt.wantErr, idx)
-			}
-			if tt.wantErr {
-				return
-			}
-			gotBytes, err := json.Marshal(stage)
-			if err != nil {
-				t.Errorf("Could not marshal stage: %v", err)
-			}
-			if !bytes.Equal(gotBytes, tt.args.data) {
-				t.Errorf("Expected `%v`, got `%v` [idx: %d]", string(tt.args.data), string(gotBytes), idx)
-			}
-			if !reflect.DeepEqual(&gotStage, stage) {
-				t.Errorf("got {%v, %v}, expected {%v, %v} [%d]", gotStage.Type, gotStage.Options, stage.Type, stage.Options, idx)
-			}
-		})
-	}
-}
-
-// Test new stages that have Inputs (osbuild v2 schema)
-func TestStageV2_UnmarshalJSON(t *testing.T) {
-	type fields struct {
-		Type    string
-		Options StageOptions
-		Inputs  Inputs
-	}
-	type args struct {
-		data []byte
-	}
-	tests := []struct {
-		name    string
-		fields  fields
-		args    args
-		wantErr bool
-	}{
-		{
-			name: "rpm-empty",
-			fields: fields{
-				Type:    "org.osbuild.rpm",
-				Options: &RPMStageOptions{},
-				Inputs:  &RPMStageInputs{},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.rpm","inputs":{"packages":null},"options":{}}`),
-			},
-		},
-		{
-			name: "rpm",
-			fields: fields{
-				Type: "org.osbuild.rpm",
-				Inputs: &RPMStageInputs{
-					Packages: &RPMStageInput{
-						References: RPMStageSourceArrayRefs{
-							&RPMStageSourceArrayRef{
-								ID: "checksum1",
-							},
-							&RPMStageSourceArrayRef{
-								ID: "checksum2",
-								Options: &RPMStageSourceOptions{
-									Metadata: &RPMStageReferenceMetadata{
-										CheckGPG: true,
-									},
-								},
-							},
-						},
-					},
-				},
-				Options: &RPMStageOptions{
-					GPGKeys: []string{"key1", "key2"},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.rpm","inputs":{"packages":{"type":"","origin":"","references":[{"id":"checksum1"},{"id":"checksum2","options":{"metadata":{"rpm.check_gpg":true}}}]}},"options":{"gpgkeys":["key1","key2"]}}`),
-			},
-		},
-		{
-			name: "ostree-preptree",
-			fields: fields{
-				Type: "org.osbuild.ostree.preptree",
-				Options: &OSTreePrepTreeStageOptions{
-					EtcGroupMembers: []string{
-						"wheel",
-					},
-				},
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.ostree.preptree","options":{"etc_group_members":["wheel"]}}`),
-			},
-		},
-		{
-			name: "xz",
-			fields: fields{
-				Type: "org.osbuild.xz",
-				Options: &XzStageOptions{
-					Filename: "image.raw.xz",
-				},
-				Inputs: NewFilesInputs(NewFilesInputReferencesPipeline("os", "image.raw")),
-			},
-			args: args{
-				data: []byte(`{"type":"org.osbuild.xz","inputs":{"file":{"type":"org.osbuild.files","origin":"org.osbuild.pipeline","references":{"name:os":{"file":"image.raw"}}}},"options":{"filename":"image.raw.xz"}}`),
-			},
-		},
-	}
-	for idx, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			stage := &Stage{
-				Type:    tt.fields.Type,
-				Options: tt.fields.Options,
-				Inputs:  tt.fields.Inputs,
-			}
-			var gotStage Stage
-			if err := gotStage.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
-				println("data: ", string(tt.args.data))
-				t.Errorf("Stage.UnmarshalJSON() error = %v, wantErr %v [idx: %d]", err, tt.wantErr, idx)
-			}
-			if tt.wantErr {
-				return
-			}
-			gotBytes, err := json.Marshal(stage)
-			if err != nil {
-				t.Errorf("Could not marshal stage: %v", err)
-			}
-			if !bytes.Equal(gotBytes, tt.args.data) {
-				t.Errorf("Expected `%v`, got `%v` [idx: %d]", string(tt.args.data), string(gotBytes), idx)
-			}
-			if !reflect.DeepEqual(&gotStage, stage) {
-				t.Errorf("got {%v, %v, %v}, expected {%v, %v, %v} [%d]", gotStage.Type, gotStage.Options, gotStage.Inputs, stage.Type, stage.Options, stage.Inputs, idx)
-			}
-		})
-	}
-}
diff --git a/internal/upload/azure/azurestorage_test.go b/internal/upload/azure/azurestorage_test.go
index aada263dd..249b4f4a4 100644
--- a/internal/upload/azure/azurestorage_test.go
+++ b/internal/upload/azure/azurestorage_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestRandomStorageAccountName(t *testing.T) {
@@ -15,3 +16,18 @@ func TestRandomStorageAccountName(t *testing.T) {
 	r := regexp.MustCompile(`^[\d\w]{24}$`)
 	assert.True(t, r.MatchString(randomName), "the returned name should be 24 characters long and contain only alphanumerical characters")
 }
+
+func TestEnsureVHDExtension(t *testing.T) {
+	tests := []struct {
+		s    string
+		want string
+	}{
+		{s: "toucan.zip", want: "toucan.zip.vhd"},
+		{s: "kingfisher.vhd", want: "kingfisher.vhd"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.s, func(t *testing.T) {
+			require.Equal(t, tt.want, EnsureVHDExtension(tt.s))
+		})
+	}
+}