juju / terraform-provider-juju

A Terraform provider for Juju
Apache License 2.0
19 stars 37 forks source link

Add space support #336

Open phvalguima opened 10 months ago

phvalguima commented 10 months ago

Description

There is currently no way to interact with spaces API using the provider.

For example, there is no way to specify spaces in juju_application bindings. The only way I see to workaround this issue is to run the deployment with a bundle where I correctly specify spaces.

The provider should also check at add_space if the subnets specified exist in juju's subnets.

Urgency

Casually reporting

Terraform Juju Provider version

edge

Terraform version

1.6.3

Terraform Configuration(s)

No response

Reproduce / Test

--

Debug/Panic Output

No response

Notes & References

No response

phvalguima commented 8 months ago

Hi, in a discussion with @hmlanigan, the first step is to come up with an Schema for spaces.

My proposal is to have spaces as a DataSource and Resource. Therefore, an user can create new spaces in its modules or declare existing spaces from a controller.

One thing is that spaces and subnets are very connected concepts in Juju. I am not entirely sure if we should keep it as one single logical group (only spaces) or break it into two DataSources: spaces and subnets. I will write a schema proposal for each. In case it is decided to move on with one single logical object, then subnets become "NestedBlocks" in spaces but they can have the same Schema.

Motivation

Having space support would allow us to interact with main cloud providers in TF more easily. One example, one could declare a subnet in AWS and then declare it directly as a subnet DataSource, pass to a space and start using it:

resource "aws_subnet" "public_cidr" {
  vpc_id            = aws_vpc.single_az_vpc.id
  cidr_block        = var.public_cidr.cidr
  availability_zone = var.vpc.az

  tags = {
    Name = var.public_cidr.name
  }
}
data "juju_subnet" "public_subnet" {
  provider_id  =  aws_vpc.single_az_vpc.id
  cidr               = var.public_cidr.cidr
}
resource "juju_space" "public_space" {
  ...
  subnets = [data.juju_subnet.public_subnet.cidr]
}

Then, an user can create a VM based on the information above and pass it to juju:

resource "aws_instance" "jumphost" {
    ...
    public_ips = data.juju_subnet.public_subnet.cidr.0  # This way we are linked between Juju and AWS
}
resource "juju_machine" "jumphost_vm" {
   ...
}

Subnet as DataSources Motivation

I think the best argument to have subnets as DataSource instead of parts of Space object is to be able to track its lifecycle separated. This way, subnet would show up in terraform state and we can also execute operations, such as remove from space-1 and move it to space-2, as spaces depend on it explicitly.

Space Schema

Here is my proposal for the space schema, based in the space doc in Juju code:

Schema {
    resp.Schema = schema.Schema{
        Description: "A resource representing a Juju Space.",
        Attributes: map[string]schema.Attribute{
            "name": schema.StringAttribute{
                Description: "The space name.",
                Required:    true,
                Computed:    true,
                PlanModifiers: []planmodifier.String{
                    stringplanmodifier.RequiresReplaceIfConfigured()
                },
            },
            "id": schema.StringAttribute{
                Description: "The Space ID.",
                Required:    true,
                Computed:    true,
            },
            "model-uuid": schema.StringAttribute{
                Description: "The model UUID containing this subnet.",
                Computed: true,
                Required: true,
            },
            "provider-id": schema.StringAttribute{
                Description: "The cloud provider ID for the subnet.",
                Computed:    true,
            },
            "zones": schema.ListAttribute{
                Description: "List of cloud provider's availability zones. Composed by each Subnet's AZ.",
                Computed: true,
            },
            "subnets": schema.ListAttribute{
                Description: "List of subnets' CIDRs that have been added to this space.",
                Computed: true,
                Optional: true
            },
        },
    }
} 

Subnet Schema

The subnet schema should consider the info that generally shows up in the output of:

$ juju subnets
 10.10.10.0/20:
    type: ipv4
    provider-id: subnet-...
    provider-network-id: vpc-...
    status: in-use
    space: alpha
    zones:
    * us-east-1a

Here is the schema is based on this struct from juju code:

Schema {
    resp.Schema = schema.Schema{
        Description: "A data source representing a Juju Subnet.",
        Attributes: map[string]schema.Attribute{
            "cidr": schema.StringAttribute{
                Description: "The subnet CIDR.",
                                Required: true
                Computed:    true,
            },
            "type": schema.StringAttribute{
                Description: "The subnet CIDR type (ipv4 or v6).",
                                Required: true
                Computed:    true,
            },
            "space-id": schema.StringAttribute{
                Description: "The subnet Space ID.",
                Optional:    true,
                Computed:    true,
                PlanModifiers: []planmodifier.String{
                    stringplanmodifier.RequiresReplaceIfConfigured()
                },
            },
            "model-uuid": schema.StringAttribute{
                Description: "The model UUID containing this subnet.",
                Computed: true,
                Required: true,
            },
            "status": schema.StringAttribute{
                Description: "The subnet has been allocated .",
                Optional:    true,
                Computed:    true,
                PlanModifiers: []planmodifier.String{
                    stringplanmodifier.RequiresReplaceIfConfigured()
                },
            },
            "provider-id": schema.StringAttribute{
                Description: "The cloud provider ID for the subnet.",
                Computed:    true,
            },
            "provider-network-id": schema.ListAttribute{
                Description: "The cloud provider's network ID for the subnet.",
                Computed: true,
            },
            "is-public": schema.BooleanAttribute{
                Description: "Marks if this subnet is publicly available.",
                Computed:    true,
            },
            "vlan": schema.Int64Attribute{
                Description: "VLAN tag used by this subnet.",
                Computed: true,
                Required: false,
            },
            "zones": schema.ListAttribute{
                Description: "List of cloud provider's availability zones.",
            },
            "is-fan-underlay": schema.BooleanAttribute{
                Description: "Indicates if this subnet represents a Fan network underlay CIDR.",
                Optional:    true,
                Computed:    true,
                Default:     false,
            },
            "is-fan-overlay": schema.BooleanAttribute{
                Description: "Indicates if this subnet represents a Fan network overlay CIDR.",
                Optional:    true,
                Computed:    true,
                Default:     false,
            },

        },
    }
}
hmlanigan commented 8 months ago

Some thoughts related to the space source schema:

  1. Name should be required, but not computed. Juju will not change it on its own. Nor is RequiresReplaceIfConfigured necessary as there is a juju rename-space command, thus it's available by API.
  2. Id is computed, but not required. Only juju can't define an ID for a space and will provide the value via API.
  3. Moving to model-uuid rather than name is great. I'm planning a schema change to that for other resources. Against, this doesn't need a computed, however it does need a RequiresReplace.
  4. Are zones and provider-id really necessary? Will they be used by other resources?
  5. We need to be careful with the subnet list, and verify that the order of the list doesn't interfere with terraform plan etc.
  6. Assuming that the subnet list has ElementType: types.StringType,?

As not all spaces are created by a user, e.g. MAAS, having a space datasource would also be good. There is also a default space called alpha which is created by juju as all subnets must be in a space.

A subnet should be a datasource. You cannot directly add, modify nor remove a subnet via the juju client. Datasources can be referenced inside of resources. You can ask juju to search for available spaces and subnets, however I'm not sure how that fits into the terraform methodology. - Update: you've mentioned it should be a data source then used the resource schema to describe it.

A subnet datasource should have a cidr and model-uuid. An id is required for testing, see some of the other datasources.