前一篇文章介紹了一些 Output 的常用處理方式,包含 apply
、pulumi.all
、pulumi.output
。今天再來看一些範例與技巧。
在使用 AWS EKS 的時候,如果我們要自行新增機器,不是透過 EKS 的 NodeGroup 的時候,通常會使用 AWS EKS 最佳化的 AMI。該 AMI 通常要提供 UserData 讓 EC2 開機的時候可以註冊到 EKS Cluster 中。
這邊有個函式是用來生成 UserData:
function generateUserDataForEksNode(
clusterName: pulumi.Input<string>,
clusterCA: pulumi.Input<string>,
apiServerEndpoint: pulumi.Input<string>
): pulumi.Output<string> {
return pulumi
.all([clusterName, clusterCA, apiServerEndpoint])
.apply(function ([clusterName, clusterCA, apiServerEndpoint]) {
return `#!/bin/bash
set -ex
B64_CLUSTER_CA=${clusterCA}
API_SERVER_URL=${apiServerEndpoint}
/etc/eks/bootstrap.sh ${clusterName} --b64-cluster-ca $B64_CLUSTER_CA --apiserver-endpoint $API_SERVER_URL\`;
`;
});
}
首先這邊用到了第一個技巧,就是這個函式的所有輸入參數都是 pulumi.Input
型別。這樣我們就不用煩惱使用者是傳遞原始型別 (primitive types)、還是其他的 Output 給我們。
第二個技巧是使用 pulumi.all 將傳入的參數轉換為單一 Output 處理。上一篇文章有提到,可以使用 pulumi.output
將任何數值轉換為 Output 類型。其實 pulumi.all
也可以達到一樣的效果,
pulumi.all
的型別定義比較複雜,這邊附上這個範例所用到的定義:
export declare function all<T1, T2, T3>(values: [Input<T1>, Input<T2>, Input<T3>]): Output<[Unwrap<T1>, Unwrap<T2>, Unwrap<T3>]>;
可以看到 pulumi.all
實際上接受的是 Input 陣列,並且會轉成 Output 型別。
當我們有 generateUserDataForEksNode
函式的時候我們要怎麼使用呢?
這邊假設我們有個 EKS cluster,我們可以從這個 cluster 中提取 name
, endpoint
, certificateAuthorities
,用於 generateUserDataForEksNode
的參數。
const cluster = new aws.eks.Cluster("my-eks", {...});
const clusterName: pulumi.Output<string> = cluster.name;
const clusterApiEndpoint: pulumi.Output<string> = cluster.endpoint;
const clusterCA: pulumi.Output<string> =
cluster.certificateAuthorities.apply(cas => cas[0].data);
const userData = generateUserDataForEksNode(clusterName, clusterCA, clusterApiEndpoint);
從這個範例可以看到,需要特別為 certificateAuthorities
做處理。因為 ClusterCertificateAuthority 的型別是 Output<ClusterCertificateAuthority[]>
,但這邊需要的是 ClusterCertificateAuthority 裡面的 data
,因此需要使用 apply 將 Output<ClusterCertificateAuthority[]>
中的 data 拿出來。
如果很多這種巢狀的 Output 都要這樣處理,其實寫起來很不方便。因此 pulumi 提供了一個方法稱為 lifting
。我們可以透過 lifting 的方式快速存取巢狀物件內的資料。
例如剛剛的 certificateAuthorities
轉換可以直接改為:
const clusterCA: pulumi.Output<string> =
cluster.certificateAuthorities[0].data;
在 Pulumi 中,經常會有將多個 Input 型別的資料組合成一個新的字串的需求,Pulumi 也提供了一個方便的方式可以快速處理這種需求。透過 pulumi.concat
即可快速組合 Input 成為新的 Output。
一樣是產生 user data 的範例,改用 pulumi.concat
撰寫後變成這樣:
function generateUserDataForEksNode(
clusterName: pulumi.Input<string>,
clusterCA: pulumi.Input<string>,
apiServerEndpoint: pulumi.Input<string>
): pulumi.Output<string> {
return pulumi.concat("#!/bin/bash\n",
"set -ex\n",
"B64_CLUSTER_CA=", clusterCA, "\n",
"API_SERVER_URL=", apiServerEndpoint, "\n",
"/etc/eks/bootstrap.sh ", clusterName, " --b64-cluster-ca $B64_CLUSTER_CA --apiserver-endpoint $API_SERVER_URL\n");
}
在 Output 這章最後介紹一個只在 JavaScript/TypeScript 才有的功能:pulumi.interpolate
。
這個功能是利用 ES6 的 tagged templates 達成的。
先來看範例:
function generateUserDataForEksNode(
clusterName: pulumi.Input<string>,
clusterCA: pulumi.Input<string>,
apiServerEndpoint: pulumi.Input<string>
): pulumi.Output<string> {
return pulumi.interpolate`#!/bin/bash
set -ex
B64_CLUSTER_CA=${clusterCA}
API_SERVER_URL=${apiServerEndpoint}
/etc/eks/bootstrap.sh ${clusterName} --b64-cluster-ca $B64_CLUSTER_CA --apiserver-endpoint $API_SERVER_URL\\
`
}
可以看到與一開始的版本大同小異,唯一的差別在於沒有了 pulumi.all([...]).apply()
的結構了,使用 pulumi.interpolate
取代。
這邊要注意,這是 ES6 的一種新寫法,pulumi.interpolate 後直接連接著 Template Literals,不是呼叫函式的時候忘記加小括號。
這個 tagged template 的功能有點像 pulumi.concat
的語法糖,ES6 執行時會把 Template 中會用到的變數蒐集成一個參數,因此在 interpolate
的實作中,就可以使用前面的技巧組合出 Output<string>
。
這邊附上 interpolate 的原始碼,可以看到內部是使用 pulumi.output
加上 pulumi.apply
的方式產生輸出。
function interpolate(literals, ...placeholders) {
return output(placeholders).apply((unwrapped) => {
let result = "";
// interleave the literals with the placeholders
for (let i = 0; i < unwrapped.length; i++) {
result += literals[i];
result += unwrapped[i];
}
// add the last literal
result += literals[literals.length - 1];
return result;
});
}
其他語言中,有提供類似的方式,例如在 Python 可以使用 Output.format
,Golang 中可以使用 pulumi.Sprintf
。不同語言如何使用可以參考官方文件。
本來今天要順便講 Stack Output,發現寫的內容比預想中的多,就留到明天再發好了。連續創作了一個禮拜,有點習慣這種感覺,但每天都要花 2~3 個小時寫文章趕 deadline。希望之後可以開始累積一些草稿,就能讓文章品質高一點了。