kubernetes/kops · error

expected an AWS cloud, got %T

Error message

expected an AWS cloud, got %T

What it means

karpenterRootDeviceName resolves an AMI's root device name (/dev/xvda, /dev/sda1, etc.) so Karpenter EC2NodeClass block device mappings override the image's root volume. The template function's cloud field must be an awsup.AWSCloud to call ResolveImage; this error is thrown when tf.cloud holds any other cloud implementation or nil, i.e. Karpenter EC2NodeClass generation was attempted on a non-AWS cluster.

Source

Thrown at upup/pkg/fi/cloudup/template_functions_karpenter.go:388

	}

	return []karpenterBlockDeviceMapping{
		{
			DeviceName: rootDeviceName,
			EBS:        ebs,
			RootVolume: new(true),
		},
	}, nil
}

// karpenterRootDeviceName resolves the root device name of the InstanceGroup image, so
// that the generated block device mapping overrides the image's root volume rather than
// attaching an additional one. The name varies between images (/dev/xvda, /dev/sda1),
// so it has to come from the image itself.
func (tf *TemplateFunctions) karpenterRootDeviceName(image string) (string, error) {
	cloud, ok := tf.cloud.(awsup.AWSCloud)
	if !ok {
		return "", fmt.Errorf("expected an AWS cloud, got %T", tf.cloud)
	}
	resolved, err := cloud.ResolveImage(image)
	if err != nil {
		return "", fmt.Errorf("unable to resolve image %q: %w", image, err)
	}
	if resolved == nil {
		return "", fmt.Errorf("unable to resolve image %q: not found", image)
	}
	rootDeviceName := fi.ValueOf(resolved.RootDeviceName)
	if rootDeviceName == "" {
		return "", fmt.Errorf("image %q has no root device name", image)
	}
	return rootDeviceName, nil
}

func (tf *TemplateFunctions) buildKarpenterNodePool(ig *kops.InstanceGroup) (*karpenterNodePool, error) {
	labels, err := nodelabels.BuildNodeLabels(tf.Cluster, ig)
	if err != nil {

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Ensure the cluster's cloudProvider is aws before using providerRole/karpenter instance groups
  2. Check the cluster spec: cloudConfig.aws should be configured and `kops get cluster -oyaml` should show cloudProvider aws
  3. Move the Karpenter instance group to an AWS-only cluster, or use the cloud's native autoscaling instead
  4. If writing tests, inject a mock or real awsup.AWSCloud into tf.cloud

Example fix

// before (non-AWS cluster spec)
cloudProvider: gce
instanceGroups: [{ name: karpenter-ig, karpenter: {...} }]
// after
cloudProvider: aws
# or remove the karpenter instance group on non-AWS clusters
Defensive patterns

Strategy: type-guard

Validate before calling

// before using karpenter template functions
if _, ok := tf.cloud.(awsup.AWSCloud); !ok {
	return fmt.Errorf("karpenter requires AWS; cluster cloud is %T", tf.cloud)
}

Type guard

func isAWSCloud(c fi.Cloud) bool {
	_, ok := c.(awsup.AWSCloud)
	return ok
}

Try / catch

// Go: check the error returned by the template function
rootDev, err := tf.karpenterRootDeviceName(image)
if err != nil {
	if strings.Contains(err.Error(), "expected an AWS cloud") {
		return fmt.Errorf("karpenter is AWS-only: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Running `kops update cluster` with an InstanceGroup using the Karpenter provider on a cluster whose cloud is not AWS (GCE, DO, Hetzner, OpenStack, Azure), or tf.cloud being nil/uninitialized when buildKarpenterEC2NodeClass invokes karpenterRootDeviceName.

Common situations: Copy-pasting a cluster config with karpenterInstanceGroups from an AWS cluster into a non-AWS cluster; forgetting to set cloudProvider/aws on the cluster spec; unit tests constructing TemplateFunctions without an AWSCloud (as in TestKarpenterRootDeviceNameWithoutAWSCloud).

Related errors


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