最近、インフラ周りを触ってますが、ダッシュボードで各サービスの画面がバラバラに配置されているので、
サービスがどういう構成で動いているのか、どこでアラートが発生しているのかがわかりにくいんです
ふと思ったのは、みなさんがよく書くインフラ図がそのまま構築に使えたらわかりやすいのでは?です
近いイメージだと Cloudcraft ですね
もちろん、インフラの知識は必要ですが、インフラ構成とステータス情報がひと目でわかります
この記事では、Custom React RendererでTerraformのconfiguration(JSON)を出力してみます
ユースケースは、Webで構成したCompoenntをJSONで出力してダウンロードしたものをTerraformで利用したりします
サンプルコードは一応動作しますが、機能不足のため参考程度でお願いします
React Rendererとは
ReactのComponentをあらゆる形式にrenderするもので、おなじみReactDOMやモバイル用のReactNativeなどがあります
他にもたくさんrendererが公開されています
参考: https://github.com/chentsulin/awesome-react-renderer
Reconciler
ReconcilerはStateが変更されたときの差分を検知し、検知すると特定のfunctionを呼び出します
この呼び出されるfunctionを定義するところが Host Config
と呼ばれるObjectです
Host Configのfunctionの振る舞いを変更することで、Custom rendererを実装することができるのです
reconcilerはpackage化されています
Custom rendererを自作する
import React from "react";
// import ReactDOM from "react-dom";
import TerraformRenderer from "./renderer";
// Root compoennt
const Config = props => <config>{props.children}</config>;
// Resource component
const Resource = props => (
<resource name={props.name}>{props.children}</resource>
);
// Instance component
const AwsInstance = props => <instance {...props} />;
const App = () => {
return (
<Config>
<Resource>
<AwsInstance name="example" instance_type="t2.micro" ami="ami-abc123" />
</Resource>
</Config>
);
};
TerraformRenderer.render(<App />, document.getElementById("root"));
index.jsはこんな感じですが、rendererを実装していないのでエラーになります
Custom renderを作成する
custom renderを作成します
const TerraformRenderer = {
render(element, renderDom) {
}
};
export default TerraformRenderer;
これでひとまずエラーは表示されませんが、画面が真っ白、、
Elementのクラスを作成
React elementからインスタンスを作成するために、config, resource, instance element用のクラスを作成します
class Config {
constructor(props) {
this.props = props;
this.children = [];
}
appendChild(child) {
this.children.push(child);
}
render() {
const config = this.children.reduce((acc, cur) => {
const render = cur.render();
return { ...acc, ...render };
}, {});
return { ...config };
}
}
export default Config;
class Resource {
constructor(props) {
this.props = props;
this.children = [];
}
appendChild(child) {
this.children.push(child);
}
render() {
const resources = this.children.reduce((acc, cur) => {
const render = cur.render();
return { ...acc, ...render };
}, {});
return {
resource: resources
};
}
}
export default Resource;
class AwsInstance {
constructor(props) {
this.props = props;
this.children = [];
}
appendChild(child) {}
render() {
return {
aws_instance: {
[this.props.name]: {
instance_type: this.props.instance_type,
ami: this.props.ami
}
}
};
}
}
export default AwsInstance;
各クラスに appendChild
と render
functionを定義しておきます
Host configの修正
ここに独自のロジックを実装していきます
- ポイント
-
createInstance
のパラメータにReact elementのtypeとpropsがくるので対応するインスタンスを作成 -
appendInitialChild
で親インスタンスに子インスタンスをセット -
appendChildToContainer
でrootコンテナにJSONをrender
-
import Config from './elements/config';
import Resource from './elements/resource';
import AwsInstance from './elements/awsInstance';
const constructors = {
config: Config,
resource: Resource,
instance: AwsInstance
};
const HostConfig = {
getRootHostContext: rootContainerInstance => {},
prepareForCommit: containerInfo => {},
resetAfterCommit: containerInfo => {},
getChildHostContext: (parentHostContext, type, rootContainerInstance) => {},
shouldSetTextContent: (type, props) => {},
createInstance: (type, props) => {
const { children, ...args } = props;
return new constructors[type](args);
},
createTextInstance: (
type,
rootContainerInstance,
hostContext,
internalInstanceHandle
) => {},
finalizeInitialChildren: (
domElement,
type,
props,
rootContainerInstance,
hostContext
) => {},
appendChildToContainer: (container, child) => {
container.innerHTML = JSON.stringify(child.render());
},
appendChild: (parent, child) => {},
appendInitialChild: (parentInstance, child) => {
parentInstance.appendChild(child);
},
supportsMutation: true
};
export default HostConfig;
react-reconcilerをインストールします
$ npm install react-reconciler
最後にrendererを修正します
import Reconciler from "react-reconciler";
import HostConfig from './HostConf';
const reconciler = Reconciler(HostConfig);
const TerraformRenderer = {
render(element, renderDom) {
const container = reconciler.createContainer(renderDom, false, false);
reconciler.updateContainer(element, container, null, null);
}
};
export default TerraformRenderer;
表示できました
まとめ
ひとまず、Custom rendererでJSONを画面に表示することができました
ドキュメントや本体のコードを参考にしましたが、Custom rendererを実装することでReactの知識がさらに深まりました
あと、renderer周りを調べていて、役割毎にpackageが分割されてるんですよねー (とても参考になります
類似のサービスはあるし、APIで取得できる情報も限られていると思いますが、Reactでインフラ構築を今後も個人プロジェクトとしてやっていきですー
あと、テストも書かないとね、、
こちらの動画もとても参考になりましたので、是非ご覧ください
React Conf 2019 Building a Custom React Renderer | Sophie Alpert
Enjoy Custom React Renderer ヘ(^o^)ノ