Skip to content

Base Resource

cloudspells.core.base

Base resource class for all CloudSpells spells.

All CloudSpells resource classes (Vcn, OkeCluster, ComputeInstance, ScalableWorkload, etc.) inherit from BaseResource, which extends pulumi.ComponentResource with:

  • Standardised naming via ResourceNamer — all child resources follow the {stack}-{name}-{suffix} pattern.
  • Consistent tagging via ResourceTagger — every resource receives managed-by, spell-type, spell-name, environment, name, and resource-type tags automatically. The spell-type is derived from the Pulumi resource type URN so callers never need to supply it explicitly.
  • SSH key management — auto-generates RSA 4096-bit key pairs when no key is provided, and exports them as Pulumi secrets.
  • Convenience wrappers that delegate to the namer and tagger so subclasses never import those helpers directly.

BaseResource

Bases: ComponentResource

Base class for all CloudSpells components.

Extends pulumi.ComponentResource with standardised naming, tagging, and optional SSH key management. All spells inherit from this class and call super().__init__() as their first step.

Attributes:

Name Type Description
project_ref Input[str] | None

Cloud-neutral provider project reference (OCI compartment OCID, AWS account ID, GCP project ID, etc.).

compartment_id Input[str] | None

OCI-specific alias for project_ref. Kept for backward compatibility with all OCI spells.

stack_name str

Resolved Pulumi stack name used in all resource names and tags.

name str

Logical resource name supplied by the caller.

display_name str

Human-readable name in the form "{stack_name}-{name}".

namer ResourceNamer

ResourceNamer instance for this resource.

tagger ResourceTagger

ResourceTagger instance for this resource.

Source code in packages/cloudspells-core/src/cloudspells/core/base.py
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
class BaseResource(pulumi.ComponentResource):
    """Base class for all CloudSpells components.

    Extends `pulumi.ComponentResource` with standardised naming, tagging,
    and optional SSH key management.  All spells inherit from this class
    and call `super().__init__()` as their first step.

    Attributes:
        project_ref: Cloud-neutral provider project reference (OCI compartment
            OCID, AWS account ID, GCP project ID, etc.).
        compartment_id: OCI-specific alias for `project_ref`.  Kept for
            backward compatibility with all OCI spells.
        stack_name: Resolved Pulumi stack name used in all resource names and
            tags.
        name: Logical resource name supplied by the caller.
        display_name: Human-readable name in the form `"{stack_name}-{name}"`.
        namer: `ResourceNamer` instance for this resource.
        tagger: `ResourceTagger` instance for this resource.
    """

    project_ref: pulumi.Input[str] | None
    compartment_id: pulumi.Input[str] | None
    stack_name: str
    name: str
    display_name: str
    namer: ResourceNamer
    tagger: ResourceTagger

    def __init__(
        self,
        resource_type: str,
        name: str,
        compartment_id: pulumi.Input[str] | None = None,
        stack_name: str | None = None,
        opts: pulumi.ResourceOptions | None = None,
        *,
        project_ref: pulumi.Input[str] | None = None,
    ) -> None:
        """Initialise a named, tagged Pulumi component resource.

        Args:
            resource_type: Pulumi resource type URN string
                (e.g. `"custom:network:Vcn"`).
            name: Logical resource name (e.g. `"lab"`).  Combined with
                stack_name to form the Pulumi resource URN
                `"{stack_name}-{name}"`.
            compartment_id: OCID of the OCI compartment that will own the
                child resources created by this component.  For non-OCI
                providers use project_ref instead.  At least one of
                compartment_id or project_ref must be provided.
            stack_name: Pulumi stack name.  Defaults to
                `pulumi.get_stack()` when `None`; override in tests to
                avoid a live Pulumi context.
            opts: Standard Pulumi resource options forwarded to the component
                base class (e.g. `protect`, `depends_on`).
            project_ref: Cloud-neutral alias for compartment_id.  When both
                are provided compartment_id takes precedence.  Use this
                parameter for non-OCI providers (AWS account ID, GCP project
                ID, etc.) to keep `BaseResource` cloud-neutral.
        """
        resolved_stack = stack_name if stack_name is not None else pulumi.get_stack()
        super().__init__(resource_type, f"{resolved_stack}-{name}", {}, opts)

        # Resolve the provider-specific project reference from either alias.
        # compartment_id is kept for backward compatibility with all OCI spells.
        resolved_ref = compartment_id if compartment_id is not None else project_ref
        self.project_ref = resolved_ref
        self.compartment_id = resolved_ref
        self.stack_name = resolved_stack
        self.name = name
        self.display_name = f"{resolved_stack}-{name}"

        # Initialize helper classes
        self.namer = ResourceNamer(resolved_stack, name)
        self.tagger = ResourceTagger(resolved_stack, name, _spell_type_from_urn(resource_type))

    # ------------------------------------------------------------------
    # Naming helpers
    # ------------------------------------------------------------------

    def create_resource_name(self, suffix: str) -> str:
        """Return a standardised child resource name.

        Delegates to `ResourceNamer.create_resource_name`.

        Args:
            suffix: Short type suffix (e.g. `"vcn"`, `"igw"`,
                `"sn-public"`).

        Returns:
            `"{stack_name}-{resource_name}-{suffix}"` string.
        """
        return self.namer.create_resource_name(suffix)

    def create_dns_label(self, prefix: str) -> str:
        """Return a DNS-safe label for OCI networking resources.

        Delegates to `ResourceNamer.create_dns_label`.

        Args:
            prefix: Short alphanumeric prefix (e.g. `"pub"`, `"vcn"`).

        Returns:
            Concatenation of prefix and the stack name.
        """
        return self.namer.create_dns_label(prefix)

    # ------------------------------------------------------------------
    # Tagging helpers
    # ------------------------------------------------------------------

    def create_freeform_tags(
        self,
        resource_name: str,
        resource_type: str,
        additional_tags: dict[str, Any] | None = None,
    ) -> dict[str, str]:
        """Return standard freeform tags for a child resource.

        Delegates to `ResourceTagger.create_freeform_tags`.

        Args:
            resource_name: Display name used as the `Name` tag value.
            resource_type: Resource category (e.g. `"vcn"`, `"subnet"`).
            additional_tags: Optional extra tags to merge into the result.

        Returns:
            `dict[str, str]` of OCI freeform tags.
        """
        return self.tagger.create_freeform_tags(resource_name, resource_type, additional_tags)

    def create_network_resource_tags(
        self,
        resource_name: str,
        resource_type: str,
        network_type: str,
        subnet_group: str | None = None,
        additional_tags: dict[str, Any] | None = None,
    ) -> dict[str, str]:
        """Return freeform tags enriched with networking metadata.

        Delegates to `ResourceTagger.create_network_resource_tags`.

        Args:
            resource_name: Display name used as the `Name` tag value.
            resource_type: Resource category (e.g. `"subnet"`).
            network_type: `"public"` or `"private"`.
            subnet_group: Optional sub-group label (e.g. `"public-a"`).
            additional_tags: Optional extra tags to merge.

        Returns:
            `dict[str, str]` including `NetworkType` (and optionally
            `SubnetGroup`) keys alongside the baseline tags.
        """
        return self.tagger.create_network_resource_tags(
            resource_name,
            resource_type,
            network_type,
            subnet_group,
            additional_tags,
        )

    def create_gateway_tags(
        self,
        resource_name: str,
        gateway_type: str,
        additional_tags: dict[str, Any] | None = None,
    ) -> dict[str, str]:
        """Return freeform tags for a gateway resource.

        Delegates to `ResourceTagger.create_gateway_tags`.

        Args:
            resource_name: Display name used as the `Name` tag value.
            gateway_type: Gateway category (e.g. `"internet"`, `"nat"`,
                `"service"`).
            additional_tags: Optional extra tags to merge.

        Returns:
            `dict[str, str]` including the `GatewayType` key alongside
            the baseline tags.
        """
        return self.tagger.create_gateway_tags(resource_name, gateway_type, additional_tags)

    # ------------------------------------------------------------------
    # SSH key management
    # ------------------------------------------------------------------

    def _setup_ssh_keys(self, ssh_public_key: pulumi.Input[str] | None) -> None:
        """Configure the SSH key pair for this resource.

        If ssh_public_key is `None` or an empty string, a new RSA
        4096-bit key pair is generated with `Helper.generate_ssh_key_pair`
        and the result is stored on `self`.  Otherwise the provided public
        key is used as-is and no private key is stored.

        Sets the following instance attributes:

        - `self.ssh_public_key`    — public key string.
        - `self.ssh_private_key`   — private key string, or `None`.
        - `self.auto_generated_keys` — `True` when keys were generated
          automatically.

        Args:
            ssh_public_key: An existing OpenSSH public key string, or
                `None` to trigger automatic key generation.
        """
        if ssh_public_key is None or (isinstance(ssh_public_key, str) and ssh_public_key.strip() == ""):
            public_key, private_key = Helper().generate_ssh_key_pair(self.stack_name, self.name)
            self.ssh_public_key = public_key
            self.ssh_private_key = private_key
            self.auto_generated_keys = True
        else:
            self.ssh_public_key = str(ssh_public_key)
            self.ssh_private_key = None
            self.auto_generated_keys = False

    def _get_ssh_outputs(self) -> dict[str, pulumi.Output[str]]:
        """Collect Pulumi stack outputs for auto-generated SSH key material.

        Returns a mapping with `ssh_public_key` and `ssh_private_key`
        wrapped as Pulumi secrets, but only when keys were auto-generated
        (i.e. `auto_generated_keys` is `True`).  The outputs are
        suitable for passing directly to `self.register_outputs()`.

        Returns:
            Mapping of output name to `pulumi.Output[str]` secret value.
            Returns an empty dict when SSH keys were supplied by the caller.
        """
        outputs: dict[str, pulumi.Output[str]] = {}
        if self.auto_generated_keys:
            outputs["ssh_public_key"] = pulumi.Output.secret(self.ssh_public_key)
            if self.ssh_private_key:
                outputs["ssh_private_key"] = pulumi.Output.secret(self.ssh_private_key)
        return outputs

    # ------------------------------------------------------------------
    # Resource introspection
    # ------------------------------------------------------------------

    def get_resource(self, resource_name: str) -> Any | None:
        """Get a child resource by attribute name.

        A generic accessor for any attribute on the component.  Following
        Pulumi best practices for component resource introspection.

        Args:
            resource_name: Attribute name of the child resource
                (e.g. `"public_subnet"`, `"nat_gateway"`).

        Returns:
            The requested resource object, or `None` if the attribute does
            not exist.

        Example:
            >>> vcn = Vcn(name="my-vcn", compartment_id="...", stack_name="prod")
            >>> vcn.finalize_network()
            >>> public_subnet = vcn.get_resource("public_subnet")
            >>> nat_gateway   = vcn.get_resource("nat_gateway")
        """
        return getattr(self, resource_name, None)

__init__(resource_type: str, name: str, compartment_id: pulumi.Input[str] | None = None, stack_name: str | None = None, opts: pulumi.ResourceOptions | None = None, *, project_ref: pulumi.Input[str] | None = None) -> None

Initialise a named, tagged Pulumi component resource.

Parameters:

Name Type Description Default
resource_type str

Pulumi resource type URN string (e.g. "custom:network:Vcn").

required
name str

Logical resource name (e.g. "lab"). Combined with stack_name to form the Pulumi resource URN "{stack_name}-{name}".

required
compartment_id Input[str] | None

OCID of the OCI compartment that will own the child resources created by this component. For non-OCI providers use project_ref instead. At least one of compartment_id or project_ref must be provided.

None
stack_name str | None

Pulumi stack name. Defaults to pulumi.get_stack() when None; override in tests to avoid a live Pulumi context.

None
opts ResourceOptions | None

Standard Pulumi resource options forwarded to the component base class (e.g. protect, depends_on).

None
project_ref Input[str] | None

Cloud-neutral alias for compartment_id. When both are provided compartment_id takes precedence. Use this parameter for non-OCI providers (AWS account ID, GCP project ID, etc.) to keep BaseResource cloud-neutral.

None
Source code in packages/cloudspells-core/src/cloudspells/core/base.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def __init__(
    self,
    resource_type: str,
    name: str,
    compartment_id: pulumi.Input[str] | None = None,
    stack_name: str | None = None,
    opts: pulumi.ResourceOptions | None = None,
    *,
    project_ref: pulumi.Input[str] | None = None,
) -> None:
    """Initialise a named, tagged Pulumi component resource.

    Args:
        resource_type: Pulumi resource type URN string
            (e.g. `"custom:network:Vcn"`).
        name: Logical resource name (e.g. `"lab"`).  Combined with
            stack_name to form the Pulumi resource URN
            `"{stack_name}-{name}"`.
        compartment_id: OCID of the OCI compartment that will own the
            child resources created by this component.  For non-OCI
            providers use project_ref instead.  At least one of
            compartment_id or project_ref must be provided.
        stack_name: Pulumi stack name.  Defaults to
            `pulumi.get_stack()` when `None`; override in tests to
            avoid a live Pulumi context.
        opts: Standard Pulumi resource options forwarded to the component
            base class (e.g. `protect`, `depends_on`).
        project_ref: Cloud-neutral alias for compartment_id.  When both
            are provided compartment_id takes precedence.  Use this
            parameter for non-OCI providers (AWS account ID, GCP project
            ID, etc.) to keep `BaseResource` cloud-neutral.
    """
    resolved_stack = stack_name if stack_name is not None else pulumi.get_stack()
    super().__init__(resource_type, f"{resolved_stack}-{name}", {}, opts)

    # Resolve the provider-specific project reference from either alias.
    # compartment_id is kept for backward compatibility with all OCI spells.
    resolved_ref = compartment_id if compartment_id is not None else project_ref
    self.project_ref = resolved_ref
    self.compartment_id = resolved_ref
    self.stack_name = resolved_stack
    self.name = name
    self.display_name = f"{resolved_stack}-{name}"

    # Initialize helper classes
    self.namer = ResourceNamer(resolved_stack, name)
    self.tagger = ResourceTagger(resolved_stack, name, _spell_type_from_urn(resource_type))

create_resource_name(suffix: str) -> str

Return a standardised child resource name.

Delegates to ResourceNamer.create_resource_name.

Parameters:

Name Type Description Default
suffix str

Short type suffix (e.g. "vcn", "igw", "sn-public").

required

Returns:

Type Description
str

"{stack_name}-{resource_name}-{suffix}" string.

Source code in packages/cloudspells-core/src/cloudspells/core/base.py
137
138
139
140
141
142
143
144
145
146
147
148
149
def create_resource_name(self, suffix: str) -> str:
    """Return a standardised child resource name.

    Delegates to `ResourceNamer.create_resource_name`.

    Args:
        suffix: Short type suffix (e.g. `"vcn"`, `"igw"`,
            `"sn-public"`).

    Returns:
        `"{stack_name}-{resource_name}-{suffix}"` string.
    """
    return self.namer.create_resource_name(suffix)

create_dns_label(prefix: str) -> str

Return a DNS-safe label for OCI networking resources.

Delegates to ResourceNamer.create_dns_label.

Parameters:

Name Type Description Default
prefix str

Short alphanumeric prefix (e.g. "pub", "vcn").

required

Returns:

Type Description
str

Concatenation of prefix and the stack name.

Source code in packages/cloudspells-core/src/cloudspells/core/base.py
151
152
153
154
155
156
157
158
159
160
161
162
def create_dns_label(self, prefix: str) -> str:
    """Return a DNS-safe label for OCI networking resources.

    Delegates to `ResourceNamer.create_dns_label`.

    Args:
        prefix: Short alphanumeric prefix (e.g. `"pub"`, `"vcn"`).

    Returns:
        Concatenation of prefix and the stack name.
    """
    return self.namer.create_dns_label(prefix)

create_freeform_tags(resource_name: str, resource_type: str, additional_tags: dict[str, Any] | None = None) -> dict[str, str]

Return standard freeform tags for a child resource.

Delegates to ResourceTagger.create_freeform_tags.

Parameters:

Name Type Description Default
resource_name str

Display name used as the Name tag value.

required
resource_type str

Resource category (e.g. "vcn", "subnet").

required
additional_tags dict[str, Any] | None

Optional extra tags to merge into the result.

None

Returns:

Type Description
dict[str, str]

dict[str, str] of OCI freeform tags.

Source code in packages/cloudspells-core/src/cloudspells/core/base.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
def create_freeform_tags(
    self,
    resource_name: str,
    resource_type: str,
    additional_tags: dict[str, Any] | None = None,
) -> dict[str, str]:
    """Return standard freeform tags for a child resource.

    Delegates to `ResourceTagger.create_freeform_tags`.

    Args:
        resource_name: Display name used as the `Name` tag value.
        resource_type: Resource category (e.g. `"vcn"`, `"subnet"`).
        additional_tags: Optional extra tags to merge into the result.

    Returns:
        `dict[str, str]` of OCI freeform tags.
    """
    return self.tagger.create_freeform_tags(resource_name, resource_type, additional_tags)

create_network_resource_tags(resource_name: str, resource_type: str, network_type: str, subnet_group: str | None = None, additional_tags: dict[str, Any] | None = None) -> dict[str, str]

Return freeform tags enriched with networking metadata.

Delegates to ResourceTagger.create_network_resource_tags.

Parameters:

Name Type Description Default
resource_name str

Display name used as the Name tag value.

required
resource_type str

Resource category (e.g. "subnet").

required
network_type str

"public" or "private".

required
subnet_group str | None

Optional sub-group label (e.g. "public-a").

None
additional_tags dict[str, Any] | None

Optional extra tags to merge.

None

Returns:

Type Description
dict[str, str]

dict[str, str] including NetworkType (and optionally

dict[str, str]

SubnetGroup) keys alongside the baseline tags.

Source code in packages/cloudspells-core/src/cloudspells/core/base.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def create_network_resource_tags(
    self,
    resource_name: str,
    resource_type: str,
    network_type: str,
    subnet_group: str | None = None,
    additional_tags: dict[str, Any] | None = None,
) -> dict[str, str]:
    """Return freeform tags enriched with networking metadata.

    Delegates to `ResourceTagger.create_network_resource_tags`.

    Args:
        resource_name: Display name used as the `Name` tag value.
        resource_type: Resource category (e.g. `"subnet"`).
        network_type: `"public"` or `"private"`.
        subnet_group: Optional sub-group label (e.g. `"public-a"`).
        additional_tags: Optional extra tags to merge.

    Returns:
        `dict[str, str]` including `NetworkType` (and optionally
        `SubnetGroup`) keys alongside the baseline tags.
    """
    return self.tagger.create_network_resource_tags(
        resource_name,
        resource_type,
        network_type,
        subnet_group,
        additional_tags,
    )

create_gateway_tags(resource_name: str, gateway_type: str, additional_tags: dict[str, Any] | None = None) -> dict[str, str]

Return freeform tags for a gateway resource.

Delegates to ResourceTagger.create_gateway_tags.

Parameters:

Name Type Description Default
resource_name str

Display name used as the Name tag value.

required
gateway_type str

Gateway category (e.g. "internet", "nat", "service").

required
additional_tags dict[str, Any] | None

Optional extra tags to merge.

None

Returns:

Type Description
dict[str, str]

dict[str, str] including the GatewayType key alongside

dict[str, str]

the baseline tags.

Source code in packages/cloudspells-core/src/cloudspells/core/base.py
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def create_gateway_tags(
    self,
    resource_name: str,
    gateway_type: str,
    additional_tags: dict[str, Any] | None = None,
) -> dict[str, str]:
    """Return freeform tags for a gateway resource.

    Delegates to `ResourceTagger.create_gateway_tags`.

    Args:
        resource_name: Display name used as the `Name` tag value.
        gateway_type: Gateway category (e.g. `"internet"`, `"nat"`,
            `"service"`).
        additional_tags: Optional extra tags to merge.

    Returns:
        `dict[str, str]` including the `GatewayType` key alongside
        the baseline tags.
    """
    return self.tagger.create_gateway_tags(resource_name, gateway_type, additional_tags)

get_resource(resource_name: str) -> Any | None

Get a child resource by attribute name.

A generic accessor for any attribute on the component. Following Pulumi best practices for component resource introspection.

Parameters:

Name Type Description Default
resource_name str

Attribute name of the child resource (e.g. "public_subnet", "nat_gateway").

required

Returns:

Type Description
Any | None

The requested resource object, or None if the attribute does

Any | None

not exist.

Example

vcn = Vcn(name="my-vcn", compartment_id="...", stack_name="prod") vcn.finalize_network() public_subnet = vcn.get_resource("public_subnet") nat_gateway = vcn.get_resource("nat_gateway")

Source code in packages/cloudspells-core/src/cloudspells/core/base.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
def get_resource(self, resource_name: str) -> Any | None:
    """Get a child resource by attribute name.

    A generic accessor for any attribute on the component.  Following
    Pulumi best practices for component resource introspection.

    Args:
        resource_name: Attribute name of the child resource
            (e.g. `"public_subnet"`, `"nat_gateway"`).

    Returns:
        The requested resource object, or `None` if the attribute does
        not exist.

    Example:
        >>> vcn = Vcn(name="my-vcn", compartment_id="...", stack_name="prod")
        >>> vcn.finalize_network()
        >>> public_subnet = vcn.get_resource("public_subnet")
        >>> nat_gateway   = vcn.get_resource("nat_gateway")
    """
    return getattr(self, resource_name, None)