equinix/terraform-provider-metal

Implement Connections resources

displague opened this issue · 7 comments

What problem are you facing?

Equinix Metal offers Connections through Equinix Fabric. These are not made available through this provider.

How could the Equinix Metal Terraform Provider help solve your problem?

Implement Datasources

  • #41 Connections (by organization or project)
    • #41 Connection Ports
  • #41 Virtual Circuits Datasource
  • Events (We haven't implemented these for other resources. I'm not sure what benefit they provide in Terraform. Listed only for completeness.)
    • Connections (Not planned)
    • Ports (Not planned)
    • Virtual Circuits (Not planned)

Implement Resources

  • Connections Resource #76
    • Connection Ports (computed)
    • Virtual Circuits (computed) (Use independent virtual-circuit datasource or resources)
  • Virtual Circuits Resource #92

Depends on packethost/packngo#228 packethost/packngo#234 (more context there)


Initial Connection shape proposal (see comments for revisions):

resource "metal_connection" "conn" {
  // create only
  type         = "string" // shared | dedicated
  speed        = 0        // int bps, rather than permitted "123 mbps" format
  organization = "string" // GET returns this as {href}
  metro        = "string" // returned as {href}
  facility     = "string" // returned as {href}, create either metro or facility, both will be returned

  // create and update
  name          = "string"
  description   = "string"
  contact_email = "string"
  redundancy    = "string" // primary | redundant
  tags = [
    "string"
  ]

  // read only
  id     = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  status = "string"
  ports {
    primary {
      id        = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
      role      = "string"
      status    = "string"
      switch_id = "string"
      // virtual_circuits is promoted to connection level property
    }
    secondary {
      // same as primary, except "role=secondary"
    }
  }

  // set type (multiple, removable, updateable)
  // these are read from "ports" above, included in /connection/{id}/ports/{id}
  // (virtual_circuits list can come from either port, they should have the same list,
  // we can map by id when sync'ing, just-in-case)
  // these are updated or deleted through /virtual-circuits/{id}
  virtual_circuit {
    // create only
    nni_vlan = 0
    project = "" // returned as {href}

    // create and update
    name        = "string"
    description = "string"
    speed       = 0
    tags = [
      "string"
    ]
    vnid = 0

    // read only
    status          = "string"
    id              = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    virtual_network = "" // returned as {href} 
    // hide this if it is redundant to nni_vlan or vnid?
  }
}

@t0mk I updated the checkboxes here and added a sketch of the metal_connection resource (which goes along with the thinking for metal_device changes, bringing ports to the surface)

The fields that I included (and grouped into create/update/readonly) are based on the current API spec documentation (hopefully accurate).

I think users may prefer to terraform import the connection, until terraform apply can create connections in a reasonable amount of time. Users can then modify the connection and virtual-circuits in Terraform.

I made some assumptions above, and they are not all accurate:

  • the only returned connection ports will be ‘primary’ and ‘secondary’ (this helps to make them usable in terraform)
  • virtual circuits returned for each port are identical and can be treated as a direct property of the connection, their relationship to ports is a side-effect.
  • users can not interact with the ports, only read their values.
  • virtual circuits only make sense in the same project as their connection
  • connections are always part of a single project connections are organization resources

This broadens the scope of connections and the ramifications of deleting a connection. This also increases the requirements for importing a connection. Users will need to import and manage all virtual-circuits across the connection in this one Terraform resource. Deleting the connection will delete all of the virtual-circuits.

The ramifications of deleting an organization resource are also large in scope. In order to the follow the API, I'm updating the proposed shape to include project id in each virtual-circuit, not within the connection.

I'm reconsidering virtual_circuit as a property of the metal_connection.

Independent metal_virtual_circuit resource would be usable with datasource'd connections and would avoid the large upfront requirement of pre-populating an imported connection. Even if future versions of Terraform can generate config during import, independent virtual_circuit resources, which are project scoped, are lighter and less risky to manage without configuring them as a property of the connection.

resource "metal_connection" "conn" {
  // create only
  type         = "string" // shared | dedicated
  speed        = 0        // int bps, rather than permitted "123 mbps" format
  organization = "string" // GET returns this as {href}
  metro        = "string" // returned as {href}
  facility     = "string" // returned as {href}, create either metro or facility, both will be returned

  // create and update
  name          = "string"
  description   = "string"
  contact_email = "string"
  redundancy    = "string" // primary | redundant
  tags = [
    "string"
  ]

  // read only
  id     = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  status = "string"
  ports {
    primary {
      id        = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
      role      = "string"
      status    = "string"
      switch_id = "string"
      // virtual_circuits is promoted to a new resource type for updates,
      // they are a computed field, type Map (indexed by circuit id) here
      virtual_circuits = {
        {id} = {
            // read-only
            nni_vlan = 0
            project = "" // returned as {href}
        
            name        = "string"
            description = "string"
            speed       = 0
            tags = [
              "string"
            ]
            vnid = 0
        
            status          = "string"
            id              = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
            virtual_network = "" // returned as {href} 
            // hide this if it is redundant to nni_vlan or vnid?
          }
      }
    }
    secondary {
      // same as primary, except "role=secondary"
    }
  }
}

// these are read, updated and deleted through /virtual-circuits/{id}
// they are created through /connection/{id}/ports/{id}
resource "metal_virtual_circuit" "circuit_short" {
    // required path parameters, dependent resources
    // connection_id = "" // this appears to be a value we can lookup by port id, 
    port_id = metal_connection.conn.ports.primary.id  // connected_port is a /port/{id} property
    
    // create only
    nni_vlan = 0
    project = "" // returned as {href}

    // create and update
    name        = "string"
    description = "string"
    speed       = 0
    tags = [
      "string"
    ]
    vnid = 0

    // read only
    status          = "string"
    id              = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    virtual_network = "" // returned as {href} 
    // hide this if it is redundant to nni_vlan or vnid?

    // connection_id can be computed if we do not require it for creation
}

I'm making an assumption that connection ports can only include a single role=primary and role=secondary port, this may not be accurate.

Another reason for separating the virtual-circuits from the connections is that these resources, while dependent have different scope. Virtual-circuits are project scoped, while connections are organization scoped. Project scoped API keys may be able to manage virtual-circuits, but not connections.

I imagine that depending how we implement the lookup fields, we may run into problems with project API keys not having access to /organization/{id}/connections, /ports/ or other resources.

Making project api keys usable with these resources is not a goal, but it is something to consider while designing this.

That looks good to me. The only thing i would add is having the ability to edit the Virtual Circuit resource, main value to edit would be the vnid associated to the virtual circuit.

In the comment above, you would be able to edit vnid:

resource "metal_virtual_circuit" "circuit_short" {
    vnid = 0
    ...
}

The metal_connection resource would have a read-only computed value too:

metal_connection.conn.ports.primary.virtual_circuits.{id}.vnid
metal_connection.conn.ports.secondary.virtual_circuits.{id}.vnid

(The initial draft allowed for updating vnid within the metal_connection, but I found reasons not to like that approach.