いちはじCDK第5回 見直しと改善

先に進む前に

今回は、作ったCDKの見直しをしていこうかと・・・
なので今回はCDKっていうより、Python寄りな話になってきます。
で、現状以下の赤字の部分を改善したいなぁと思っています。

first_vpc_stack.pyより
~省略~
12:class FirstVpcStack(Stack):
13:    def __init__(
14:        self, scope: Construct, construct_id: str, myif: FirstVpcTypes, **kwargs
15:    ) -> None:
16:        super().__init__(scope, construct_id, **kwargs)
17:        # VPC
18:        vpfif = myif.firstvpc.vpcs["first-vpc"]
19:        firstvpc = MyVpc(self, myif=vpfif)
20:        # route-table public
21:        pub_routetblif = myif.firstvpc.route_tables["first-public-routetbl"]
22:        pub_routetblif.vpc_id = firstvpc.attr_vpc_id
23:        firstpubroutetbl = MyRouteTable(self, myif=pub_routetblif)
~省略~

VPCができないと、vpc_idがわからないので、パラメータリストには書けず。Stackのソースの方で設定しないといけないって部分ですね。
(。-`ω-)確かにちょっとカッコ悪いよね・・・
パラメータリストの方も以下の感じで・・・

first_vpc_params.yamlより
~省略~
20:  route_tables:
21:    first-public-routetbl:
22:      construct_id: "first-public-routetbl"
23:      #vpc_id: ""
24:      tags:
25:        Name: "first-vpc-pub-routetbl"
26:        routetbl-pub-tag1: "routetbl-pub-value1"
27:        routetbl-pub-tag2: "routetbl-pub-value2"
~省略~

vpc_idをコメントアウトしてるところがモドカシイ・・・
vpc_idが決まっていれば、以下のように書いてjinja2を使ってfirst_vpc_params.yamlを読み込むときに置換するようにできるんですけど・・・

~省略~
20:  route_tables:
21:    first-public-routetbl:
22:      construct_id: "first-public-routetbl"
23:      vpc_id: "{{firstvpc.vpcs.first_vpc.vpc_id}}"
24:      tags:
25:        Name: "first-vpc-pub-routetbl"
26:        routetbl-pub-tag1: "routetbl-pub-value1"
27:        routetbl-pub-tag2: "routetbl-pub-value2"
~省略~

置換元のパラメータがない状態で23行目のように書いといて決まったら置換するようにも考えましたが・・・jinja2は置換元が存在しないパラメータを書いておくと消えてしまうので・・・使えない。
jinja2で該当なしはスキップするような動きができないのか?調べてみましたができない感じでした・・・

やりたい事を整理

イントロダクションはこのくらいでやりたい事を整理してみます。
・パラメータリストで未決定のパラメータについて記載しておきたい。
・未決定のパラメータが決定したら、パラメータリストに該当のパラメータを追加したい。
・新たなパラメータが追加されたら、該当する置換文字列を追加パラメータに値で置換したい。(jinja2のような動きを実現する)
と言ったところですね。

見直しの構想

見直しとしては以下のような機能を追加していきたいです。
・パラメータリストを更新・管理するクラスを作る。
・スキップできるjinja2のようなものを作る。
・Constructのラッパー関数をクラスに変更する。
と言った感じです。
全体的な流れは以下の図のようになります。(GIFアニメーションで・・・)

かなりゴチャゴチャっとしちゃって伝わるか不安ですが・・・
この構想で見直して行きます。

プロジェクト構成

プロジェクトの構成で変わるところをササッと説明。
前後で表示します。構成として大きく変わるのは、servicetypesでfirst_vpc_types.pyを作らなければならなかったんですけど、それが不要になりますので削除しました。
ラッパー関数をクラス化してmyec2.pyにまとめて、旧ec2ディレクトリ下のラッパー関数は削除。
パラメータリスト(config)を更新、管理するクラスとしてmyctrl.pyを追加。
ラッパー関数をクラス化して、ポリモーフィズを使うのでベースクラスmybase.pyを追加。
最後にjinja2を自作関数に変更したので、reprice.pyを追加してます。

cdk_core_project/
├ .venv/
│ └…省略…
├ core/
│ ├ basetypes
│ │ ├ __init__.py 
│ │ ├ mycommon.py
│ │ └ myec2.py
│ ├ myconstructs
│ │ ├ ec2
│ │ │ ├ myInternetgateway.py
│ │ │ ├ … 省略 …
│ │ │ └ myvpc_gateway_attachment.py
│ │ │
│ │ │
│ │ │
│ │ ├ __init__.py
│ │ └ mytags.py
│ ├ parameters
│ │ └ first_vpc_params.yaml 
│ ├ servicetypes
│ │ ├ __init__.py 
│ │ └ first_vpc_types.py
│ ├ utils
│ │ ├ __init__.py 
│ │ └ configread.py
│ │
│ ├ __init__.py
│ └ first_vpc_stack.py
├ tests/
│ ├unit/
│ │├ __init__.py
│ │└ test_cdk_core_project_stack.py
│ └ __init__.py
├ .gitignore
├ app.py
├ cdk.json
├ README.md
├ requirements.txt
├ requirements-dev.txt
└ source.bat

cdk_core_project/
├ .venv/
│ └…省略…
├ core/
│ ├ basetypes
│ │ ├ __init__.py 
│ │ ├ mycommon.py
│ │ └ myec2.py
│ ├ myconstructs
│ │ ├ ec2★削除
│ │ │ ├ myInternetgateway.py★削除
│ │ │ ├ … 省略 …★削除
│ │ │ └ myvpc.py★削除
│ │ ├ mybase.py★追加
│ │ ├ myctrl.py★追加
│ │ ├ myec2.py★追加
│ │ ├ __init__.py
│ │ └ mytags.py
│ ├ parameters
│ │ └ first_vpc_params.yaml 
│ ├ servicetypes★削除
│ │ ├ __init__.py★削除 
│ │ └ first_vpc_types.py★削除
│ ├ utils
│ │ ├ __init__.py 
│ │ ├ configread.py
│ │ └ reprice.py★追加
│ ├ __init__.py
│ └ first_vpc_stack.py
├ tests/
│ ├unit/
│ │├ __init__.py
│ │└ test_cdk_core_project_stack.py
│ └ __init__.py
├ .gitignore
├ app.py
├ cdk.json
├ README.md
├ requirements.txt
├ requirements-dev.txt
└ source.bat

MyCtrlクラス

全体のソースコードとしては長くないです。27行ですね。

myctrl.py
01:from typing import Any
02:
03:from core.basetypes.mycommon import MyCommonIF
04:from core.myconstructs.mybase import MyBase
05:from core.utils.reprice import rep_rice
06:
07:
08:class MyCtrl:
09:    def __init__(self, config: dict):
10:        self.config: dict = config
11:        self.common = MyCommonIF(**config["common"])
12:
13:    def create(self, base: MyBase) -> Any:
14:        myif = self.get_myif(name=base.get_name())
15:        tmp = base.create(myif=myif)
16:        tmpret, tmpconfig = rep_rice(self.config)
17:        self.config = tmpconfig
18:        return tmp
19:
20:    def get_myif(self, name: str):
21:        tmp: Any = None
22:        for leaf in name.split("."):
23:            if tmp is None:
24:                tmp = self.config[leaf]
25:            else:
26:                tmp = tmp[leaf]
27:        return tmp

13~18行目は、コンストラクタを生成するところです。
基底クラス(MyBase)のcreate関数をコールしていますが、ポリモーフィズなので、MyCtrlクラスのcreate関数に渡されるクラス(MyBaseを継承している)のcreate関数がコールされる流れです。
Stackクラスからコンストラクタを生成するときは以下のように書きます。VPCのコンストラクタを生成するのケースを例にしています。

firstvpc = myctrl.create(MyVpc(obj=self, name="firstvpc.vpcs.first_vpc"))

MyVpc(obj=self,name=”firstvpc.vpcs.first_vpc”)の部分はMyVpcクラスを作ってます。
でそれをMyCtrlクラス(myctrl.createで)に渡たすことでConstructの生成をします。
MyCtrlクラスでは、先ほどの13~18行目でMyVpcクラスのcreateをコールする形なります。
なお、パラメータリストを取得するために、“firstvpc.vpcs.first_vpcという文字列を渡します。
こちらは、yamlファイルのパラメータの位置を表す文字列で、以下の赤字部分を表現しています。
で、MyCtrlクラスでは渡された、firstvpc.vpcs.first_vpcからget_myif関数を使って、青字部分を取得して、MyVpcクラスに渡す流れになりす。

---
common:
  region: ap-northeast-1
  account : "123456789099"
  tags :
    commontag1: "commontags-value1"
    commontag2: "commontags-value2"
firstvpc:
  vpcs:
    first_vpc:
      construct_id: "firstvpc"
      cidr_block: "10.0.0.0/16"
      enable_dns_hostnames: True
      enable_dns_support: True
      instance_tenancy: "default"
      tags:
        Name: "first-vpc"
        vpctag1: "vpctag-value1"
        vpctag2: "vpctag-value2"
  route_tables:
…以下略…

例としてMyVpcクラス

コンストラクタの生成クラスは複数あるので、代表としてMyVpcクラスで説明します。基本的な形はどのクラスも同じです。24行ですね。

myec2.pyのMyVpcクラス
01:class MyVpc(MyBase):
02:    def __init__(self, obj: Any, name: str):
03:        super().__init__(obj, name)
04:
05:    def create(self, myif: dict) -> Any:
06:        self.myif = myif
07:        return self._vpc_(myif=MyVPCIF(**myif), updif=self.myif)
08:
09:    def _vpc_(self, myif: MyVPCIF, updif: dict) -> CfnVPC:
10:        tags = MyTags(myif.tags)
11:        rsc = CfnVPC(
12:            self.obj,
13:            myif.construct_id,
14:            cidr_block=myif.cidr_block,
15:            enable_dns_hostnames=myif.enable_dns_hostnames,
16:            enable_dns_support=myif.enable_dns_support,
17:            instance_tenancy=myif.instance_tenancy,
18:            ipv4_ipam_pool_id=myif.ipv4_ipam_pool_id,
19:            ipv4_netmask_length=myif.ipv4_netmask_length,
20:            tags=tags,
21:        )
22:        # vpc_id
23:        updif["vpc_id"] = rsc.attr_vpc_id
24:        return rsc

05~07行目がコンストラクタを作る、_vpc_関数を呼ぶための関数です。
こちらは基底クラス(MyBaseクラス)で仮想関数として定義されており、それをオーバーライドする感じです。
MyCtrlクラスの15行目「tmp = base.create(myif=myif)」でコールされる関数です。

09~24行目からの_vpc_関数はコンストラクタを作る関数で、コンストラクタ生成後、22行目、23行目でconfigファイルに新たなパラメータvpc_idを追加しています。
ここで追加したvpc_idは、MyCtrlクラスに制御が戻ったときに(MyCtrlクラスの16行目で)、
tmpret, tmpconfig = rep_rice(self.config)
を実行してパラメータリスト内の置換項目{{firstvpc.vpcs.first_vpc.vpc_id}}を実際のvpc_idの値に置換する流れになります。

スタッククラス

パパっと書いちゃいます

01:from aws_cdk import Stack
02:from constructs import Construct
03:
04:from core.myconstructs.myctrl import MyCtrl
05:from core.myconstructs.myec2 import (
06:    MyInternetGateway,
07:    MyRoute,
08:    MyRouteTable,
09:    MyVpc,
10:    MyVPCGatewayAttachment,
11:)
12:
13:
14:class FirstVpcStack(Stack):
15:    def __init__(
16:        self, scope: Construct, construct_id: str, myctrl: MyCtrl, **kwargs
17:    ) -> None:
18:        super().__init__(scope, construct_id, **kwargs)
19:        # VPC
20:        self.myctrl = myctrl
21:        firstvpc = myctrl.create(MyVpc(obj=self, name="firstvpc.vpcs.first_vpc"))
22:        # route-table public
23:        firstpubroutetbl = myctrl.create(
24:            MyRouteTable(self, name="firstvpc.route_tables.first-public-routetbl")
25:        )
26:        # route-table private
27:        firstpriroutetbl = myctrl.create(
28:            MyRouteTable(self, name="firstvpc.route_tables.first-private-routetbl")
29:        )
30:        # igw
31:        firstigw = myctrl.create(
32:            MyInternetGateway(self, name="firstvpc.igws.first-igw")
33:        )
34:        # route-igw
35:        routeigw = myctrl.create(MyRoute(self, name="firstvpc.routes.first-igw-rute"))
36:        # vpcgatewayattach
37:        vpcgatewayattach = myctrl.create(
38:            MyVPCGatewayAttachment(
39:                self, name="firstvpc.vpcgatewayattachs.first-vpcgatewayattach"
40:            )
41:        )

最初記載した、以下赤字のような生成後に設定する部分がなくなってスッキリしたと思います。
ここは説明できるのはそれぐらいですかね・・・

~省略~
12:class FirstVpcStack(Stack):
13:    def __init__(
14:        self, scope: Construct, construct_id: str, myif: FirstVpcTypes, **kwargs
15:    ) -> None:
16:        super().__init__(scope, construct_id, **kwargs)
17:        # VPC
18:        vpfif = myif.firstvpc.vpcs["first-vpc"]
19:        firstvpc = MyVpc(self, myif=vpfif)
20:        # route-table public
21:        pub_routetblif = myif.firstvpc.route_tables["first-public-routetbl"]
22:        pub_routetblif.vpc_id = firstvpc.attr_vpc_id
23:        firstpubroutetbl = MyRouteTable(self, myif=pub_routetblif)
~省略~

app.py

大きな修正点はないですが、赤字が変更部分になります。

#!/usr/bin/env python3
import os

from aws_cdk import App, Environment

from core.first_vpc_stack import FirstVpcStack
from core.myconstructs.myctrl import MyCtrl
from core.utils.configread import configread

app = App()
myctrl = MyCtrl(config=configread("core/parameters/first_vpc_params.yaml"))
env = Environment(region=myctrl.common.region, account=myctrl.common.account)
FirstVpcStack(app, "FirstVpcStack", myctrl=myctrl, env=env)
app.synth()

deploy and destroy

では、最後にdeploy and destroyしてちゃんと動くか確認します!
まずdeploy~

> cdk deploy

✨  Synthesis time: 6.43s

FirstVpcStack: deploying... [1/1]
FirstVpcStack: creating CloudFormation changeset...
FirstVpcStack | 0/8 | 21:54:29 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack     | FirstVpcStack User Initiated
FirstVpcStack | 0/8 | 21:54:34 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack     | FirstVpcStack User Initiated
FirstVpcStack | 0/8 | 21:54:37 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata             | CDKMetadata/Default (CDKMetadata)
FirstVpcStack | 0/8 | 21:54:37 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway      | first-igw (firstigw)
FirstVpcStack | 0/8 | 21:54:37 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                  | firstvpc
FirstVpcStack | 0/8 | 21:54:38 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata             | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
FirstVpcStack | 1/8 | 21:54:38 | CREATE_COMPLETE      | AWS::CDK::Metadata             | CDKMetadata/Default (CDKMetadata)
FirstVpcStack | 1/8 | 21:54:39 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway      | first-igw (firstigw) Resource creation Initiated
FirstVpcStack | 1/8 | 21:54:39 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                  | firstvpc Resource creation Initiated
FirstVpcStack | 1/8 | 21:54:39 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway      | first-igw (firstigw) Eventual consistency check initiated
FirstVpcStack | 2/8 | 21:54:51 | CREATE_COMPLETE      | AWS::EC2::VPC                  | firstvpc 
FirstVpcStack | 2/8 | 21:54:52 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable           | first-public-routetbl (firstpublicroutetbl) 
FirstVpcStack | 2/8 | 21:54:52 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable           | first-private-routetbl (firstprivateroutetbl)
FirstVpcStack | 2/8 | 21:54:52 | CREATE_IN_PROGRESS   | AWS::EC2::VPCGatewayAttachment | first-vpcgatewayattach (firstvpcgatewayattach)
FirstVpcStack | 2/8 | 21:54:53 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable           | first-private-routetbl (firstprivateroutetbl) Resource creation Initiated
FirstVpcStack | 2/8 | 21:54:53 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable           | first-public-routetbl (firstpublicroutetbl) Resource creation Initiated
FirstVpcStack | 2/8 | 21:54:53 | CREATE_IN_PROGRESS   | AWS::EC2::VPCGatewayAttachment | first-vpcgatewayattach (firstvpcgatewayattach) Resource creation Initiated
FirstVpcStack | 2/8 | 21:54:54 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable           | first-public-routetbl (firstpublicroutetbl) Eventual consistency check initiated
FirstVpcStack | 2/8 | 21:54:54 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable           | first-private-routetbl (firstprivateroutetbl) Eventual consistency check initiated
FirstVpcStack | 3/8 | 21:54:54 | CREATE_COMPLETE      | AWS::EC2::InternetGateway      | first-igw (firstigw)
FirstVpcStack | 4/8 | 21:54:55 | CREATE_COMPLETE      | AWS::EC2::VPCGatewayAttachment | first-vpcgatewayattach (firstvpcgatewayattach) 
FirstVpcStack | 5/8 | 21:55:04 | CREATE_COMPLETE      | AWS::EC2::RouteTable           | first-private-routetbl (firstprivateroutetbl) 
FirstVpcStack | 6/8 | 21:55:04 | CREATE_COMPLETE      | AWS::EC2::RouteTable           | first-public-routetbl (firstpublicroutetbl) 
FirstVpcStack | 6/8 | 21:55:05 | CREATE_IN_PROGRESS   | AWS::EC2::Route                | first-igw-rute (firstigwrute)
FirstVpcStack | 6/8 | 21:55:06 | CREATE_IN_PROGRESS   | AWS::EC2::Route                | first-igw-rute (firstigwrute) Resource creation Initiated
FirstVpcStack | 7/8 | 21:55:07 | CREATE_COMPLETE      | AWS::EC2::Route                | first-igw-rute (firstigwrute) 
FirstVpcStack | 8/8 | 21:55:08 | CREATE_COMPLETE      | AWS::CloudFormation::Stack     | FirstVpcStack

 ✅  FirstVpcStack

✨  Deployment time: 41.86s

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:123456789099:stack/FirstVpcStack/7f05f3d0-e940-11ef-bd95-0e579dd1a853

✨  Total time: 48.29s

続いてdestroy~・・・は張らなくてもいいかな・・・

(´▽`)上手く行きましたね~

この回はここまです~。GITの該当ブランチはこちらです~。
次回はついにサブネットを作成してみます~
(´・ω・`)早くやれよ・・・サブネットごときでいつまでかかってんだよ・・・
ま・・・そういう意見も・・・ありますよね!(≧◇≦)
次回「いちはじCDK第6回 ついにサブネット」も良かったら~

コメント

タイトルとURLをコピーしました