2022年11月,我們宣布了Cloudflare API過渡到OpenAPI模式。當時,我們有一個大膽的目標,就是使OpenAPI模式成為我們SDK生態(tài)系統(tǒng)和參考文檔的真實來源。為此,在2024年的Developer Week期間,我們宣布:我們的SDK庫現(xiàn)在從這些OpenAPI模式自動生成。今天,我們隆重宣布,該生態(tài)系統(tǒng)的最新組成部分現(xiàn)將自動生成——Terraform provider和API參考文檔。
這意味著,一旦我們的產(chǎn)品中添加新的功能或?qū)傩?,并由團隊記錄到文檔,您將能看到它在我們的SDK生態(tài)系統(tǒng)應當如何使用,并能立即投入使用。不再存在延誤。不再遺漏API端點。點擊https://developers.cloudflare.com/api-next/查看新文檔站點,您可以通過安裝5.0.0-alpha1來嘗試Terraform提供者的預覽發(fā)布候選版。
為什么使用Terraform?
對于不熟悉Terraform的人來說,它是一個用于以代碼形式管理基礎設施的工具,就像管理應用代碼一樣。我們的許多客戶(無論大?。┒家蕾嘥erraform以技術(shù)無關的方式協(xié)調(diào)他們的基礎設施。從本質(zhì)上講,它是一個具備生命周期管理的HTTP客戶端,這意味著它利用我們公開文檔化的API,以理解如何在資源的整個生命周期內(nèi)進行創(chuàng)建、讀取、更新和刪除操作。
保持Terraform更新——老方法
在過去,Cloudflare一直手動維護Terraform provider,但由于該provider的內(nèi)部結(jié)構(gòu)需要自己獨特的操作方式,維護和支持的責任落在了少數(shù)人的肩上。服務團隊總是難以跟上更改的數(shù)量,因為在provider中進行一個更改都需要大量的認知開銷。一個團隊要更改provider,至少需要3個拉取請求(如果您要添加對cf-terraforming的支持,則需要4個請求)。
即使在4個拉取請求完成后,也不能保證覆蓋所有可用屬性,這意味著可能會忘記小而重要的細節(jié)且沒有暴露給客戶,導致在嘗試配置資源時受挫。為了解決這個問題,我們的Terraform provider需要依賴于同我們的SDK生態(tài)系統(tǒng)已經(jīng)從中受益的其他部的相同的OpenAPI模式。
自動更新Terraform
Terraform與我們的SDK的不同之處在于,它管理資源的生命周期。隨之而來的是與已知值以及管理請求和響應有效負載中的差異相關的一系列新問題。我們來比較一下創(chuàng)建新DNS記錄并將其取回的兩種不同方法。
使用我們的Go SDK:
使用Terraform時:
表面上看,Terraform方法更簡單,您是對的。我們已為您處理了了解如何創(chuàng)建新資源和維護更改的復雜性。但問題在于,為了讓Terraform提供這種抽象和數(shù)據(jù)保證,在應用時必須知道所有值。這意味著,即使您沒有使用代理值value,Terraform也需要知道這個值需要是什么,以便將其保存在狀態(tài)文件中,并在日后管理該屬性。以下錯誤是Terraform操作人員在應用值未知時,通常會從provider那里看到的錯誤。
而在使用SDK時,如果您不需要某個字段,則可以忽略它,永遠不需要擔心維護已知值。
為我們的OpenAPI模式解決這個問題不是易事。自從引入Terraform生成支持以來,我們模式的質(zhì)量已經(jīng)提高了一個數(shù)量級?,F(xiàn)在,我們顯式地調(diào)用所有存在的默認值,基于請求有效負載的變量響應屬性,以及任何服務器端計算的屬性。這一切都意味著為與我們API交互的任何人提供更好的體驗。
從terraform-plugin-sdk遷移到terraform-plugin-framework
要構(gòu)建Terraform provider并向操作人員公開資源或數(shù)據(jù)源,您需要兩個主要因素:一個provider服務器和一個provider。
Provider服務器負責暴露一個gRPC服務器,Terraform核心(通過CLI)使用該服務器在管理資源或從操作人員提供的配置中讀取數(shù)據(jù)源時使用該服務器進行通信。
Provider負責包裝資源和數(shù)據(jù)源,與遠程服務通信,并管理狀態(tài)文件。為此,您可以依賴terraform-plugin-sdk(通常稱為SDKv2)或terraform-plugin-framework,其中包括Terraform提供的所有接口和方法,以便正確管理其內(nèi)部機制。使用哪一個插件取決于provider的存在時間。SDKv2存在的時間更長,大多數(shù)Terraform provider都使用它,但由于時間長和復雜性,它有許多核心未解決的問題必須
保留,以便為依賴它的客戶提供向后兼容性。terraform-plugin-framework是新版本,雖然缺乏SDKv2的功能廣度,但提供了一種更像Go的方法來構(gòu)建provider,并解決了SDKv2中的許多底層錯誤。
Cloudflare Terraform provider的大部分內(nèi)容都是使用SDKv2構(gòu)建的,但在2023年初,我們采用了多路復用方式,在我們的provider中同時提供兩者。要理解為什么需要這樣做,我們必須對SDKv2有所了解。SDKv2的組織方式并不利于一致且可靠地表示null或“未設置”的值。您可以使用實驗性的ResourceData.GetRawConfig來檢查配置中是否已設置值、為null或未知,但實際上并不支持將其寫回為null。我們首次發(fā)現(xiàn)這個限制是在邊緣規(guī)則引擎(規(guī)則集)開始引入新服務的時候,這些服務需要支持的API響應中包含未設置(或缺失)、true或false狀態(tài)的布爾值,每個狀態(tài)都有自己的原因和目的。雖然這不是Cloudflare的常規(guī)API設計,但它是一種合法的方式,我們應該能夠處理。但是,如上所述,SDKv2 provider不能處理。這是因為,當一個值沒有出現(xiàn)在響應中或讀入狀態(tài)時,它會獲得一個與Go兼容的零值作為默認值。表現(xiàn)為在寫入狀態(tài)為假值后無法取消設置值(反之亦然)。
要可靠地使用這些布爾值的三個狀態(tài),我們擁有的唯一解決方案是遷移到terraform-plugin-framework,該框架具有寫回未設置值的正確實現(xiàn)。
在我們開始在老版provider中使用terraform-plugin-framework添加更多功能后,開發(fā)人員體驗顯然得到了改善,因此我們添加了一個限制,以防止未來任何人繼續(xù)使用SDKv2并無意中讓自己陷入這個問題。當我們決定將自動生成Terraform provider時,最理想的做法是將所有資源都基于terraform-plugin-framework,并徹底擺脫SDKv2中的問題。這確實使遷移復雜化了,因為內(nèi)部結(jié)構(gòu)改進后,主要組件也發(fā)生了變化,例如我們需要熟悉的模式和CRUD操作。但是,這是一項值得的投資,因為通過這樣做,我們已經(jīng)為provider的基礎做好了面向未來的準備,并減少了因存在缺陷的遺留內(nèi)部機制而導致的妥協(xié),以提供優(yōu)秀的Terraform體驗。
以迭代方式發(fā)現(xiàn)缺陷
代碼生成管道常見的一個問題是,除非您有現(xiàn)成的工具來實現(xiàn)你的新產(chǎn)品,否則很難判斷它是否有效或是否值得使用。當然,您也可以生成測試來演練新產(chǎn)品,但如果管道中存在錯誤,您可能很難發(fā)現(xiàn)一些缺陷-因為您生成的測試結(jié)果會將這個缺陷顯示為預期內(nèi)的行為。
我們已有的一個重要反饋回路就是現(xiàn)有的驗收測試套件。對現(xiàn)有provider中的所有資源進行回歸和功能測試。最棒的一點是,由于測試套件正在創(chuàng)建和管理真實資源,我們只需查看HTTP流量,看看API調(diào)用是否被遠程端點接受,就能很容易判斷結(jié)果是否是一個有效的實現(xiàn)。移植測試套件只需復制所有現(xiàn)有的測試,并檢查任何類型斷言的差異(例如列表到單個嵌套列表),然后啟動測試運行以確定資源是否正常工作。
雖然集中式模式管道顯著提高了效率,模式修復幾乎瞬間即可傳播到整個生態(tài)系統(tǒng),但它無法幫助我們解決最大的障礙,即揭露隱藏其他缺陷的缺陷。這非常耗時,因為修復Terraform中的問題時,有三個地方可能遇到錯誤:
1.在進行任何API調(diào)用之前,Terraform會實施邏輯模式驗證,當遇到驗證錯誤時,它將立即停止。
2.如果任何API調(diào)用失敗,它會在CRUD操作處停止并返回診斷信息,并立即停止。
3.在CRUD操作運行后,Terraform會進行檢查,以確保所有的值都是已知的。
這意味著,如果我們在第一步遇到缺陷,然后予以修復,不能保證或無法知道是否還有另外兩個錯誤在等著我們。更不用說,如果我們在第2步中發(fā)現(xiàn)了一個錯誤并發(fā)布了修復,就一定能確保不會在下一輪測試中的第1步發(fā)現(xiàn)錯誤了。
對此沒有靈丹妙藥,我們的解決辦法是注意模式行為中的有問題模式,并在它進入代碼生成管道之前,在OpenAPI架構(gòu)中應用CI lint規(guī)則。采用這種方法逐步減少第1步和第2步的錯誤數(shù)量,直到基本上只要處理第3步中的錯誤類型。
可重用性更高的模型和結(jié)構(gòu)體轉(zhuǎn)換方法
在Terraform provider的CRUD操作中,相當常見的樣板文件如下:
總體而言:
-我們使用req.Plan.Get()獲取建議的更新(稱為計劃)
-執(zhí)行使用新值的更新API調(diào)用
-將數(shù)據(jù)從Go類型轉(zhuǎn)換為Terraform模型(convertResponseToThingModel)
-調(diào)用resp.State.Set()來設置狀態(tài)
最初,這似乎沒有太大問題。然而,第3步將Go類型轉(zhuǎn)換為Terraform模型時,很快就會變得繁瑣、容易出錯且復雜,因為所有資源都需要這樣做才能在類型和相關的Terraform模型之間切換。為了避免生成比必要更復雜的代碼,我們的provider中進行的一項改進是,所有CRUD方法使用統(tǒng)一的apijson.Marshal、apijson.Unmarshal和apijson.UnmarshalComp方法,這些方法通過基于結(jié)構(gòu)體標簽集中轉(zhuǎn)換和處理邏輯來解決這個問題。
我們不需要生成數(shù)百個類型到模型轉(zhuǎn)換方法的實例,而是給Terraform模型添加正確的標簽,以一致的方式處理數(shù)據(jù)的序列化和反序列化。這只是對代碼的一個小改動,但從長遠來看,卻使生成的代碼更具可重用性和可讀性。這種方法的另一個好處是,它非常適合錯誤修復,因為一旦您識別出特定類型字段的錯誤,只要在統(tǒng)一界面中修復該錯誤,就能解決其他您可能還沒有發(fā)現(xiàn)的錯誤。
更多(文檔)持續(xù)更新中
為了提升我們的OpenAPI模式使用,我們正在加強SDK與新API文檔站點的集成。它使用了我們過去兩年投資并解決了一些常見使用問題的相同管道。
SDK感知
如果您使用過我們的API文檔站點,您就知道我們提供使用curl等命令行工具與API交互的示例。這是一個很好的起點,但如果您使用的是其中一個SDK庫,您需要設法將其轉(zhuǎn)換為您想要使用的方法或類型定義?,F(xiàn)在我們使用相同的管道來生成SDK和文檔,我們將通過提供您可能使用的所有庫中的示例來解決這個問題,而不限于curl。
使用cURL獲取所有區(qū)域的示例。
使用Typescript庫獲取所有區(qū)域的示例。
使用Python庫獲取所有區(qū)域的示例。
使用Go庫獲取所有區(qū)域的示例。
通過這一改進,我們還可以記住語言選擇,因此,如果您選擇使用Typescript庫查看文檔并繼續(xù)瀏覽,我們將一直向您顯示使用Typescript的示例,直至更換到其他語言。
最棒的是,當我們向現(xiàn)有端點引入新屬性或添加SDK語言時,這個文檔站點會自動與管道保持同步。使其保持最新不再需要付出巨大的工作量。
渲染更快、更高效
我們一直難以解決的一個問題是API端點的龐大數(shù)量以及如何表示它們。截至本文,我們有1,330個端點,對于每個端點,我們有一個請求有效負載,一個響應有效負載,以及多個關聯(lián)的類型。在渲染這么多信息時,我們過去使用的解決方案不得不做出一些權(quán)衡,以便讓部分表示能夠正常工作。
API文檔站點的下一個迭代通過幾種方式解決了這個問題:
-它被實施為一個現(xiàn)代React應用,將交互式客戶端體驗與靜態(tài)預渲染內(nèi)容結(jié)合起來,從而實現(xiàn)快速初始加載和快捷導航。(是的,即使不啟用JavaScript,它也能正常工作?。?。
-它會隨著您瀏覽時逐步獲取底層數(shù)據(jù)。
通過解決這個基本問題,我們還解鎖了對文檔站點和SDK生態(tài)系統(tǒng)的其他計劃改進,以提升用戶體驗,而不再需要像過去那樣做出權(quán)衡。
權(quán)限
在文檔網(wǎng)站中,用戶最希望重新實現(xiàn)的功能之一就是API端點的最低所需權(quán)限。文檔網(wǎng)站的早期版本中曾經(jīng)提供這個功能。然而,大多數(shù)使用它的人并不知道,這些值是手動維護的,且經(jīng)常不正確,導致用戶提交支持工單并感到沮喪。
在Cloudflare的身份和訪問管理系統(tǒng)中,“我需要什么才能訪問這個端點”并不是一個簡單的問題。這樣做的原因是,在請求發(fā)送到控制平面的正常流程中,我們需要兩個不同的系統(tǒng)來回答問題的一部分,然后將這些信息結(jié)合起來給出完整的答案。由于我們最初無法作為OpenAPI管道的一部分自動執(zhí)行此操作,因此我們選擇將其排除在外,而不是提供一個錯誤且無法驗證的值。
快進到今天,我們很高興地宣布,端點權(quán)限已經(jīng)回來了!我們構(gòu)建了一些新的工具,以抽象的方式回答這個問題,讓我們可以將其集成到代碼生成管道中,并讓所有端點自動獲取這些信息。與代碼生成平臺的其他部分非常類似,它專注于讓服務團隊擁有并維護高質(zhì)量的模式,可供重復使用并增加價值,無需他們進行任何工作。
無需再等待更新
隨著這一系列更新的發(fā)布,我們將不再需要等待更新才能進入SDK生態(tài)系統(tǒng)。通過這些新的改進,我們能夠在團隊記錄新屬性和端點時立即優(yōu)化它們的能力。