Carina awscc providerの自動スキーマ生成

2026-02-04 09:40:42 +0900

Carina を1週間ほどコードを見ずにClaude Codeにつくらせて、それっぽく動くようにはなったけど、そろそろコードを読んで指示出さないといけないフェーズに入ったかな、と思い、まずはawscc providerのコードを読んでいる。

実際にコードを読んでみると、awscc providerはAWS Cloud Control APを利用しているので、aws-sdk-cloudcontrol crateだけを利用すればいいはずなのに、なぜかaws-sdk-ec2 crateも利用している、といったおかしなことになっていた。

また、プロバイダー用スキーマは、 CloudFormation resource provider schemas から自動生成できるはずだがそうなっておらず、Claude Codeが手動?でスキーマを定義していた。(これはそのように指示を出していない自分が悪いのだが。)

というわけで自動生成するようにしてもらった。こんな感じで、スキーマをRustコードの形で自動生成してくれる。

//! internet_gateway schema definition for AWS Cloud Control
//!
//! Auto-generated from CloudFormation schema: AWS::EC2::InternetGateway
//!
//! DO NOT EDIT MANUALLY - regenerate with carina-codegen

use super::tags_type;
use carina_core::schema::{AttributeSchema, AttributeType, ResourceSchema};

/// Returns the schema for ec2_internet_gateway (AWS::EC2::InternetGateway)
pub fn ec2_internet_gateway_schema() -> ResourceSchema {
    ResourceSchema::new("awscc.ec2_internet_gateway")
        .with_description("Allocates an internet gateway for use with a VPC. After creating the Internet gateway, you then attach it to a VPC.")
        .attribute(
            AttributeSchema::new("internet_gateway_id", AttributeType::String)
                .with_description(" (read-only)"),
        )
        .attribute(
            AttributeSchema::new("tags", tags_type())
                .with_description("Any tags to assign to the internet gateway."),
        )
}

VPCの cidr_block は、CloudFormation resource provider schemaでは string 型になっている。

        "CidrBlock": {
            "description": "The IPv4 network range for the VPC, in CIDR notation. For example, ``10.0.0.0/16``. We modify the specified CIDR block to its canonical form; for example, if you specify ``100.68.0.18/18``, we modify it to ``100.68.0.0/18``.\n You must specify either``CidrBlock`` or ``Ipv4IpamPoolId``.",
            "type": "string"
        },

だが、Carinaは強い型付けを目指しているので、ドメイン固有型である cidr 型に変換している。これは指示しなくてもClaude Codeが勝手にそうしてくれた。

        .attribute(
            AttributeSchema::new("cidr_block", types::cidr())
                .with_description("The IPv4 network range for the VPC, in CIDR notation. For example, ``10.0.0.0/16``. We modify the specified CIDR block to its canonical form; for exam..."),
        )

VPCの instance_tenancy 属性は、CloudFormation resource provider schemaでは string 型になっているが、取り得る値は3つに限定されていることがdescriptionから読み取れる。

        "InstanceTenancy": {
"description": "The allowed tenancy of instances launched into the VPC.\n  +  ``default``: An instance launched into the VPC runs on shared hardware by default, unless you explicitly specify a different tenancy during instance launch.\n  +  ``dedicated``: An instance launched into the VPC runs on dedicated hardware by default, unless you explicitly specify a tenancy of ``host`` during instance launch. You cannot specify a tenancy of ``default`` during instance launch.\n  \n Updating ``InstanceTenancy`` requires no replacement only if you are updating its value from ``dedicated`` to ``default``. Updating ``InstanceTenancy`` from ``default`` to ``dedicated`` requires replacement.",
"type": "string"
},

これを何も考えずにRustコードに変換すると、当然のことながら string 型になる。

        .attribute(
            AttributeSchema::new("instance_tenancy", AttributeType::String)
                .with_description("The allowed tenancy of instances launched into the VPC.  + ``default``: An instance launched into the VPC runs on shared hardware by default, unless y..."),
)

そこで、コードジェネレーターでdescriptionを解析して取り得る値を抽出し、名前空間付き列挙型に変換するようにしてみた。

こんな感じで、列挙型のバリデーション関数を生成してくれる。

const VALID_INSTANCE_TENANCY: &[&str] = &["default", "dedicated", "host"];

fn validate_instance_tenancy(value: &Value) -> Result<(), String> {
    validate_namespaced_enum(
        value,
        "InstanceTenancy",
        "awscc.ec2_vpc",
        VALID_INSTANCE_TENANCY,
    )
}

そして、スキーマ定義では、カスタム属性型として定義してくれる。

        .attribute(
            AttributeSchema::new("instance_tenancy", AttributeType::Custom {
                name: "InstanceTenancy".to_string(),
                base: Box::new(AttributeType::String),
                validate: validate_instance_tenancy,
                namespace: Some("awscc.ec2_vpc".to_string()),
            })
                .with_description("The allowed tenancy of instances launched into the VPC.  + ``default``: An instance launched into the VPC runs on shared hardware by default, unless y..."),
        )