kubernetes/kops · error

backup-store %q is azureblob:// but configStore.base %q is n

Error message

backup-store %q is azureblob:// but configStore.base %q is not

What it means

For the legacy Azure etcd backup URL derivation, both the backupStore and configStore.base must live in Azure Blob Storage (vfs.AzureBlobPath), since the storage account is read from the config store. If the backup store is azureblob:// but configStore.base resolves to another backend type, this error is returned.

Source

Thrown at pkg/model/components/etcdmanager/model.go:78

func resolveAzureBackupStore(configStoreBase, backupStore string) (legacyURL string, storageAccount string, err error) {
	if !strings.HasPrefix(backupStore, "azureblob://") {
		return backupStore, "", nil
	}
	bp, err := vfs.Context.BuildVfsPath(backupStore)
	if err != nil {
		return "", "", fmt.Errorf("parsing etcd backup-store %q: %w", backupStore, err)
	}
	bpAzure, ok := bp.(*vfs.AzureBlobPath)
	if !ok {
		return "", "", fmt.Errorf("expected azureblob:// backup-store, got %q", backupStore)
	}
	csp, err := vfs.Context.BuildVfsPath(configStoreBase)
	if err != nil {
		return "", "", fmt.Errorf("parsing configStore.base %q: %w", configStoreBase, err)
	}
	csAzure, ok := csp.(*vfs.AzureBlobPath)
	if !ok {
		return "", "", fmt.Errorf("backup-store %q is azureblob:// but configStore.base %q is not", backupStore, configStoreBase)
	}
	legacy := "azureblob://" + bpAzure.Container()
	if bpAzure.Key() != "" {
		legacy += "/" + bpAzure.Key()
	}
	return legacy, csAzure.Account(), nil
}

// EtcdManagerBuilder builds the manifest for the etcd-manager
type EtcdManagerBuilder struct {
	*model.KopsModelContext
	Lifecycle    fi.Lifecycle
	AssetBuilder *assets.AssetBuilder
}

var _ fi.CloudupModelBuilder = &EtcdManagerBuilder{}

// Build creates the tasks

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Set configStore.base to an azureblob:// path in the same (or intended) storage account as the backupStore
  2. Keep backupStore and configStore.base on the same backend type
  3. Verify the cluster is actually an Azure cluster and both stores were created for it

Example fix

// before
configStore:
  base: "s3://my-bucket/config"
backups:
  backupStore: "azureblob://myaccount/etcd-backups"
// after
configStore:
  base: "azureblob://myaccount/etcd-config"
backups:
  backupStore: "azureblob://myaccount/etcd-backups"
Defensive patterns

Strategy: type-guard

Validate before calling

if strings.HasPrefix(backupStore, "azureblob://") && !strings.HasPrefix(configStoreBase, "azureblob://") {
    return fmt.Errorf("configStore.base must also be azureblob:// when backupStore is azureblob://")
}

Type guard

func bothAzure(bp, cp vfs.VFSPath) bool {
    _, okB := bp.(*vfs.AzureBlobPath)
    _, okC := cp.(*vfs.AzureBlobPath)
    return okB && okC
}

Try / catch

legacy, account, err := resolveAzureBackupStore(configStoreBase, backupStore)
if err != nil {
    if strings.Contains(err.Error(), "is azureblob:// but configStore.base") {
        // align configStore.base to azureblob:// and retry
    }
    return err
}

Prevention

When it happens

Trigger: An Azure etcd cluster where backups.backupStore starts with azureblob:// but configStore.base is e.g. s3://, file://, or another non-Azure VFS path, encountered during buildPod.

Common situations: Mixed-cloud configuration left from a migration; configStore pointing at a local path while backups use Azure; copy-pasted spec from an AWS cluster with Azure backupStore substituted.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/1017222dd087b1ac. Report an issue: GitHub.