fufu-cloud-cli 0.9.7__tar.gz → 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fufu_cloud_cli-0.9.7/src/fufu_cloud_cli.egg-info → fufu_cloud_cli-0.10.0}/PKG-INFO +58 -7
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/README.md +57 -6
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/pyproject.toml +1 -1
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/__init__.py +1 -1
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/api_backend.py +7 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/aws_errors.py +1 -1
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/capabilities.json +73 -27
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/cli.py +41 -8
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/cloud_data.py +1 -1
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/control.py +2 -2
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/docker_backend.py +15 -3
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/gcp_infra.py +42 -9
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/gcs.py +95 -6
- fufu_cloud_cli-0.10.0/src/fufu_cloud_cli/infrastructure.py +283 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/lambda_adapter.py +17 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/object_transfer.py +11 -6
- fufu_cloud_cli-0.10.0/src/fufu_cloud_cli/observability.py +127 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/runtime_sdk.py +1 -1
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/terraform_bridge/runner.py +15 -1
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0/src/fufu_cloud_cli.egg-info}/PKG-INFO +58 -7
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli.egg-info/SOURCES.txt +2 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/LICENSE +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/MANIFEST.in +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/setup.cfg +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/__main__.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/aws_lambda.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/aws_output.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/bigquery.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/capacity.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/cloudflare.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/errors.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/gcloud_environment.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/gcloud_output.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/gcp_sdk.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/http_runtime.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/iac_state.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/limits.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/manifest.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/public_access.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/pubsub.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/pulumi-endpoints.json +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/pulumi_bridge.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/remote_compute.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/runtime/functions/Dockerfile +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/runtime/ingress/Dockerfile +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/runtime/ingress/proxy.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/secrets.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/serverless.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/terraform_bridge/__init__.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/terraform_bridge/aws.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/terraform_bridge/aws_data.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/terraform_bridge/common.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/terraform_bridge/google.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/terraform_bridge/google_pubsub.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/terraform_bridge/remote_state.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/vendor/AWS-CLI-LICENSE.txt +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/vendor/__init__.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli/vendor/aws_text.py +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli.egg-info/dependency_links.txt +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli.egg-info/entry_points.txt +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli.egg-info/requires.txt +0 -0
- {fufu_cloud_cli-0.9.7 → fufu_cloud_cli-0.10.0}/src/fufu_cloud_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fufu-cloud-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: FUFU Cloud CLI for tenant-scoped mock cloud development and deployment
|
|
5
5
|
License-Expression: LicenseRef-Proprietary
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -27,6 +27,55 @@ Dynamic: license-file
|
|
|
27
27
|
`gcloud-fufu`、`az-fufu`、`wrangler-fufu`、`bq-fufu`。這是 AWS、Google Cloud、Azure 與 Cloudflare 的**明確子集**,
|
|
28
28
|
只供 FUFU mock 環境使用;不會把部署轉送到正式雲端。
|
|
29
29
|
|
|
30
|
+
0.10.0 新增 Compute Engine/EC2 VM 與 GKE/EKS 介面的實際運算命令。
|
|
31
|
+
需要啟用相應 infrastructure backend 的 FUFU 服務端;VM 使用 KVM/KubeVirt,
|
|
32
|
+
叢集使用獨立 Kubernetes API 與共用節點,均不是官方雲端資源。
|
|
33
|
+
支援 VM 建立、SSH 指令、停止/啟動與刪除,以及叢集建立、查詢、
|
|
34
|
+
get-credentials/update-kubeconfig、waiter 與刪除。
|
|
35
|
+
取得憑證後可使用標準 kubectl 部署 Deployment、Service、Job 及受管 GPU 容器。
|
|
36
|
+
工作站需另安裝 kubectl;配套 Cloud Shell 已提供。
|
|
37
|
+
|
|
38
|
+
VM 尚無公開 IP、複寫儲存、live migration 或自助 GPU passthrough;
|
|
39
|
+
叢集尚無 workload PVC、公開 LoadBalancer、互動式 exec/attach/port-forward。
|
|
40
|
+
這批新 VM/cluster API 尚未支援官方 Terraform/OpenTofu/Pulumi provider。
|
|
41
|
+
原有 firewall、global LB、Redis 等 typed configuration 仍明示 `MOCK ONLY`,
|
|
42
|
+
不代表已建立相應網路或 Redis 資料面。
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
gcloud-fufu compute instances create demo-vm --image-family=debian-12
|
|
46
|
+
gcloud-fufu compute ssh demo-vm --command='uname -s; nproc'
|
|
47
|
+
gcloud-fufu container clusters create demo-gke
|
|
48
|
+
gcloud-fufu container clusters get-credentials demo-gke
|
|
49
|
+
kubectl get pods -A
|
|
50
|
+
aws-fufu eks create-cluster --name demo-eks
|
|
51
|
+
aws-fufu eks wait cluster-active --name demo-eks
|
|
52
|
+
aws-fufu eks update-kubeconfig --name demo-eks
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
這些操作需要 FUFU credential;不使用官方雲端金鑰。資源以租戶隔離,配額不足
|
|
56
|
+
會明確回覆 RESOURCE_EXHAUSTED。測試後請刪除所建 VM/cluster 以釋放資源。
|
|
57
|
+
|
|
58
|
+
0.9.8 提供 Cloud Run/Functions 的原生形式日誌查詢、CloudWatch Logs
|
|
59
|
+
tail/follow,以及 `fufu observability` 的日誌、請求指標與追蹤查詢。
|
|
60
|
+
公開套件包含完整的日誌指令模組;重複建立 CloudWatch log group 的
|
|
61
|
+
`ResourceAlreadyExistsException` 會保留至 CLI 與 boto3 例外處理。
|
|
62
|
+
配套服務端持久化收集 Cloud Run、Functions Gen 1/Gen 2、Lambda 的
|
|
63
|
+
stdout/stderr 與例外堆疊。支援租戶隔離、IAM、敏感值遮罩、查詢分頁、
|
|
64
|
+
request ID/trace 關聯及重啟恢復。GCP log names 清單、dry run、欄位存在
|
|
65
|
+
查詢,以及 AWS 10,000 筆/1 MiB 寫入批次均有契約驗收。
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
gcloud-fufu run services logs read hello --limit=100 --log-filter='severity>=ERROR'
|
|
69
|
+
gcloud-fufu functions logs read hello --gen2 --limit=100
|
|
70
|
+
aws-fufu logs tail /aws/lambda/hello --since 1h --follow --fufu-duration 300
|
|
71
|
+
fufu observability status --provider gcp
|
|
72
|
+
fufu observability logs --provider aws --request-id REQUEST_ID --since 1h
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
請求 metrics/traces 是明示的 FUFU 擴充,沒有實作完整 Cloud Monitoring、
|
|
76
|
+
CloudWatch Metrics、Trace 或 X-Ray。Log Router sinks、Logs Insights、
|
|
77
|
+
subscriptions、alerts 及完整 OpenTelemetry collector 尚未支援。
|
|
78
|
+
|
|
30
79
|
0.9.7 補齊 Lambda 五種 `wait` 指令、設定/程式碼更新的 `--revision-id`,
|
|
31
80
|
以及常用設定的 `--cli-input-json` 和 `Variables={KEY=value}` shorthand。
|
|
32
81
|
過期 RevisionId 回 `PreconditionFailedException`,保留原部署;CLI 仍等待
|
|
@@ -47,9 +96,10 @@ insertAll/SELECT 與持久化日誌。`fufu_cloud_cli.gcp_sdk` 提供官方 Py
|
|
|
47
96
|
BigQuery、Cloud Tasks、Logging SDK 的明確 REST factory;可用 `[gcp]` extra
|
|
48
97
|
安裝已驗收相依。需要對應的新後端,並非透明 ADC 或所有 Google SDK 替換。
|
|
49
98
|
|
|
50
|
-
`fufu infrastructure`
|
|
51
|
-
|
|
52
|
-
|
|
99
|
+
`fufu infrastructure` 另保留 typed configuration mock 目錄,包含 Cloud SQL、
|
|
100
|
+
App Engine、DNS、VPC、Artifact Registry、Cloud Build、Gemini、VM Manager、
|
|
101
|
+
Eventarc 等。這個目錄只保存配置,不啟動 SQL/registry/網路/推論;
|
|
102
|
+
實際 VM/Kubernetes 請使用上述 compute/ec2/container/eks 指令。
|
|
53
103
|
UI 顯示僅設定,CLI stderr 保留 MOCK ONLY,不因輸出投影而隱藏。
|
|
54
104
|
新服務尚未接入 Terraform/OpenTofu/Pulumi;單機背景投遞不是雲端 HA。
|
|
55
105
|
|
|
@@ -62,8 +112,9 @@ fufu infrastructure catalog
|
|
|
62
112
|
```
|
|
63
113
|
|
|
64
114
|
BigQuery 是 tenant emulator 的 GoogleSQL 子集,無 DML/DDL、load/export、
|
|
65
|
-
|
|
66
|
-
|
|
115
|
+
外部表與效能/費用等價。配套新版 Logging 預設每租戶 2 GiB;GCP 保留
|
|
116
|
+
30 天,AWS 依 log group retention 設定,未設定時不自動到期。只收集已
|
|
117
|
+
驗證歸屬的 FUFU 工作負載容器。Tasks 每 queue 每秒最多 1 次、每租戶 2 個同時投遞;耗盡 task
|
|
67
118
|
留待檢查,與官方刪除策略不同。新的 dependency flow 不代表完整應用都能遷移。
|
|
68
119
|
|
|
69
120
|
0.9.4 新增 GCS generation/metageneration 條件、`storage objects describe/update/list`、8 KiB 自訂 metadata 與二進位 `cat`。競爭寫入、過期條件、CLI metadata 和官方 gcloud 輸出欄位已對照驗收;不相符條件回傳 `PRECONDITION_FAILED`/HTTP 412。新功能需要同步更新 FUFU Console、GCS 儲存映像和 Cloud Shell。儲存升級前須停寫並備份完整 volume;新 metadata 格式可讀舊資料,回滾須還原升級前備份。
|
|
@@ -80,7 +131,7 @@ Python 預設識別而遇到 Cloudflare HTTP 403/1010。識別標頭不取代
|
|
|
80
131
|
## Cloudflare 前端部署與 R2
|
|
81
132
|
|
|
82
133
|
```bash
|
|
83
|
-
pip install 'fufu-cloud-cli==0.
|
|
134
|
+
pip install 'fufu-cloud-cli==0.10.0'
|
|
84
135
|
export FUFU_CLOUDFLARE_CREDENTIAL_FILE=/run/secrets/fufu-cloudflare.json
|
|
85
136
|
wrangler-fufu capabilities
|
|
86
137
|
wrangler-fufu pages project create frontend --production-branch main
|
|
@@ -4,6 +4,55 @@
|
|
|
4
4
|
`gcloud-fufu`、`az-fufu`、`wrangler-fufu`、`bq-fufu`。這是 AWS、Google Cloud、Azure 與 Cloudflare 的**明確子集**,
|
|
5
5
|
只供 FUFU mock 環境使用;不會把部署轉送到正式雲端。
|
|
6
6
|
|
|
7
|
+
0.10.0 新增 Compute Engine/EC2 VM 與 GKE/EKS 介面的實際運算命令。
|
|
8
|
+
需要啟用相應 infrastructure backend 的 FUFU 服務端;VM 使用 KVM/KubeVirt,
|
|
9
|
+
叢集使用獨立 Kubernetes API 與共用節點,均不是官方雲端資源。
|
|
10
|
+
支援 VM 建立、SSH 指令、停止/啟動與刪除,以及叢集建立、查詢、
|
|
11
|
+
get-credentials/update-kubeconfig、waiter 與刪除。
|
|
12
|
+
取得憑證後可使用標準 kubectl 部署 Deployment、Service、Job 及受管 GPU 容器。
|
|
13
|
+
工作站需另安裝 kubectl;配套 Cloud Shell 已提供。
|
|
14
|
+
|
|
15
|
+
VM 尚無公開 IP、複寫儲存、live migration 或自助 GPU passthrough;
|
|
16
|
+
叢集尚無 workload PVC、公開 LoadBalancer、互動式 exec/attach/port-forward。
|
|
17
|
+
這批新 VM/cluster API 尚未支援官方 Terraform/OpenTofu/Pulumi provider。
|
|
18
|
+
原有 firewall、global LB、Redis 等 typed configuration 仍明示 `MOCK ONLY`,
|
|
19
|
+
不代表已建立相應網路或 Redis 資料面。
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gcloud-fufu compute instances create demo-vm --image-family=debian-12
|
|
23
|
+
gcloud-fufu compute ssh demo-vm --command='uname -s; nproc'
|
|
24
|
+
gcloud-fufu container clusters create demo-gke
|
|
25
|
+
gcloud-fufu container clusters get-credentials demo-gke
|
|
26
|
+
kubectl get pods -A
|
|
27
|
+
aws-fufu eks create-cluster --name demo-eks
|
|
28
|
+
aws-fufu eks wait cluster-active --name demo-eks
|
|
29
|
+
aws-fufu eks update-kubeconfig --name demo-eks
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
這些操作需要 FUFU credential;不使用官方雲端金鑰。資源以租戶隔離,配額不足
|
|
33
|
+
會明確回覆 RESOURCE_EXHAUSTED。測試後請刪除所建 VM/cluster 以釋放資源。
|
|
34
|
+
|
|
35
|
+
0.9.8 提供 Cloud Run/Functions 的原生形式日誌查詢、CloudWatch Logs
|
|
36
|
+
tail/follow,以及 `fufu observability` 的日誌、請求指標與追蹤查詢。
|
|
37
|
+
公開套件包含完整的日誌指令模組;重複建立 CloudWatch log group 的
|
|
38
|
+
`ResourceAlreadyExistsException` 會保留至 CLI 與 boto3 例外處理。
|
|
39
|
+
配套服務端持久化收集 Cloud Run、Functions Gen 1/Gen 2、Lambda 的
|
|
40
|
+
stdout/stderr 與例外堆疊。支援租戶隔離、IAM、敏感值遮罩、查詢分頁、
|
|
41
|
+
request ID/trace 關聯及重啟恢復。GCP log names 清單、dry run、欄位存在
|
|
42
|
+
查詢,以及 AWS 10,000 筆/1 MiB 寫入批次均有契約驗收。
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
gcloud-fufu run services logs read hello --limit=100 --log-filter='severity>=ERROR'
|
|
46
|
+
gcloud-fufu functions logs read hello --gen2 --limit=100
|
|
47
|
+
aws-fufu logs tail /aws/lambda/hello --since 1h --follow --fufu-duration 300
|
|
48
|
+
fufu observability status --provider gcp
|
|
49
|
+
fufu observability logs --provider aws --request-id REQUEST_ID --since 1h
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
請求 metrics/traces 是明示的 FUFU 擴充,沒有實作完整 Cloud Monitoring、
|
|
53
|
+
CloudWatch Metrics、Trace 或 X-Ray。Log Router sinks、Logs Insights、
|
|
54
|
+
subscriptions、alerts 及完整 OpenTelemetry collector 尚未支援。
|
|
55
|
+
|
|
7
56
|
0.9.7 補齊 Lambda 五種 `wait` 指令、設定/程式碼更新的 `--revision-id`,
|
|
8
57
|
以及常用設定的 `--cli-input-json` 和 `Variables={KEY=value}` shorthand。
|
|
9
58
|
過期 RevisionId 回 `PreconditionFailedException`,保留原部署;CLI 仍等待
|
|
@@ -24,9 +73,10 @@ insertAll/SELECT 與持久化日誌。`fufu_cloud_cli.gcp_sdk` 提供官方 Py
|
|
|
24
73
|
BigQuery、Cloud Tasks、Logging SDK 的明確 REST factory;可用 `[gcp]` extra
|
|
25
74
|
安裝已驗收相依。需要對應的新後端,並非透明 ADC 或所有 Google SDK 替換。
|
|
26
75
|
|
|
27
|
-
`fufu infrastructure`
|
|
28
|
-
|
|
29
|
-
|
|
76
|
+
`fufu infrastructure` 另保留 typed configuration mock 目錄,包含 Cloud SQL、
|
|
77
|
+
App Engine、DNS、VPC、Artifact Registry、Cloud Build、Gemini、VM Manager、
|
|
78
|
+
Eventarc 等。這個目錄只保存配置,不啟動 SQL/registry/網路/推論;
|
|
79
|
+
實際 VM/Kubernetes 請使用上述 compute/ec2/container/eks 指令。
|
|
30
80
|
UI 顯示僅設定,CLI stderr 保留 MOCK ONLY,不因輸出投影而隱藏。
|
|
31
81
|
新服務尚未接入 Terraform/OpenTofu/Pulumi;單機背景投遞不是雲端 HA。
|
|
32
82
|
|
|
@@ -39,8 +89,9 @@ fufu infrastructure catalog
|
|
|
39
89
|
```
|
|
40
90
|
|
|
41
91
|
BigQuery 是 tenant emulator 的 GoogleSQL 子集,無 DML/DDL、load/export、
|
|
42
|
-
|
|
43
|
-
|
|
92
|
+
外部表與效能/費用等價。配套新版 Logging 預設每租戶 2 GiB;GCP 保留
|
|
93
|
+
30 天,AWS 依 log group retention 設定,未設定時不自動到期。只收集已
|
|
94
|
+
驗證歸屬的 FUFU 工作負載容器。Tasks 每 queue 每秒最多 1 次、每租戶 2 個同時投遞;耗盡 task
|
|
44
95
|
留待檢查,與官方刪除策略不同。新的 dependency flow 不代表完整應用都能遷移。
|
|
45
96
|
|
|
46
97
|
0.9.4 新增 GCS generation/metageneration 條件、`storage objects describe/update/list`、8 KiB 自訂 metadata 與二進位 `cat`。競爭寫入、過期條件、CLI metadata 和官方 gcloud 輸出欄位已對照驗收;不相符條件回傳 `PRECONDITION_FAILED`/HTTP 412。新功能需要同步更新 FUFU Console、GCS 儲存映像和 Cloud Shell。儲存升級前須停寫並備份完整 volume;新 metadata 格式可讀舊資料,回滾須還原升級前備份。
|
|
@@ -57,7 +108,7 @@ Python 預設識別而遇到 Cloudflare HTTP 403/1010。識別標頭不取代
|
|
|
57
108
|
## Cloudflare 前端部署與 R2
|
|
58
109
|
|
|
59
110
|
```bash
|
|
60
|
-
pip install 'fufu-cloud-cli==0.
|
|
111
|
+
pip install 'fufu-cloud-cli==0.10.0'
|
|
61
112
|
export FUFU_CLOUDFLARE_CREDENTIAL_FILE=/run/secrets/fufu-cloudflare.json
|
|
62
113
|
wrangler-fufu capabilities
|
|
63
114
|
wrangler-fufu pages project create frontend --production-branch main
|
|
@@ -184,6 +184,13 @@ class ApiClient:
|
|
|
184
184
|
return json.loads(raw)
|
|
185
185
|
except HTTPError as exc:
|
|
186
186
|
status = exc.code
|
|
187
|
+
if '/infrastructure/' in path and status == 429:
|
|
188
|
+
try:
|
|
189
|
+
value = json.loads(exc.read(4096))
|
|
190
|
+
if isinstance(value, dict) and value.get('code') == 'RESOURCE_EXHAUSTED':
|
|
191
|
+
raise FufuError('RESOURCE_EXHAUSTED', 'FUFU 租戶資源配額或主機容量已滿;請刪除不需要的資源,或調整平台資源配額後再重試。') from None
|
|
192
|
+
except (ValueError, OSError): pass
|
|
193
|
+
finally: exc.close()
|
|
187
194
|
if self.provider == 'aws' and '/compute/' in path and status in {400, 412}:
|
|
188
195
|
try:
|
|
189
196
|
value = json.loads(exc.read(4096))
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""Only documented provider error codes may cross the public transport boundary."""
|
|
2
|
-
AWS_ERRORS = frozenset('''ConditionalCheckFailedException ResourceNotFoundException ResourceInUseException ValidationException
|
|
2
|
+
AWS_ERRORS = frozenset('''ConditionalCheckFailedException ResourceNotFoundException ResourceInUseException ResourceAlreadyExistsException ValidationException
|
|
3
3
|
NoSuchKey NoSuchBucket NotFound NotModified ParameterNotFound ParameterAlreadyExists ParameterVersionNotFound QueueDoesNotExist
|
|
4
4
|
PreconditionFailed InvalidRange InvalidParameterException InvalidParameterValue InvalidAttributeName InvalidAttributeValue QueueNameExists
|
|
5
5
|
PurgeQueueInProgress ReceiptHandleIsInvalid BatchEntryIdsNotDistinct EmptyBatchRequest BatchRequestTooLong InvalidBatchEntryId
|
|
@@ -226,7 +226,8 @@
|
|
|
226
226
|
"no-Entra-STS"
|
|
227
227
|
]
|
|
228
228
|
},
|
|
229
|
-
"aws-cloudwatch-logs": {"status": "implemented", "backend": "api", "mode": "simplified", "operations": ["log-group-crud", "log-stream-crud", "put-get-filter-events", "retention-policy", "CLI-UI-boto3"], "limitations": ["
|
|
229
|
+
"aws-cloudwatch-logs": {"status": "implemented", "backend": "api", "mode": "simplified", "operations": ["log-group-crud", "log-stream-crud", "put-get-filter-events", "retention-policy-expiry", "CLI-UI-boto3", "owned-Lambda-stdout-stderr", "logs-tail-follow", "SQLite-persistence", "signed-pagination", "credential-redaction"], "limitations": ["single-host-configurable-storage-capacity", "simple-text-filters-only", "no-Insights-subscriptions-or-KMS", "follow-default-300s-max-3600s", "service-page-tokens-except-tail-auto-pagination"]},
|
|
230
|
+
"fufu-observability": {"status": "implemented", "backend": "api", "mode": "simplified", "operations": ["logs", "request-metrics", "request-traces", "collector-status", "GCP-AWS-IAM", "structured-trace-links"], "limitations": ["explicit-FUFU-extension-not-native-Monitoring-CloudWatch-Trace-XRay-API", "gateway-request-duration-not-handler-or-billing-metrics", "no-automatic-SDK-spans-alerts-or-cross-service-trace-assembly", "plain-stdout-correlates-by-service-revision", "async-Lambda-logs-without-request-metrics", "single-host-no-HA"]},
|
|
230
231
|
"aws-lambda-async": {"status": "implemented", "backend": "api", "mode": "simplified", "operations": ["invoke-Event", "invoke-DryRun", "CLI-UI-boto3-Invoke"], "limitations": ["1MiB-payload", "single-host-SQLite-two-dispatchers", "at-least-once", "two-handler-retries-60s-120s", "system-retries-up-to-6h", "no-destinations-or-DLQ", "no-AWS-scaling-or-managed-timeout-equivalence", "10000-events-or-512MiB-encoded-queue-per-tenant"]},
|
|
231
232
|
"aws-cli-output": {"status": "implemented", "mode": "simplified", "operations": ["JMESPath-query", "json", "text", "yaml"], "limitations": ["no-table-or-yaml-stream", "no-full-CLI-auto-pagination", "Lambda-response-subset-no-Code-download"]},
|
|
232
233
|
"aws-data-services": {
|
|
@@ -345,17 +346,21 @@
|
|
|
345
346
|
"mode": "simplified",
|
|
346
347
|
"operations": [
|
|
347
348
|
"write-read",
|
|
348
|
-
"filter-AND-severity-timestamp-
|
|
349
|
+
"filter-AND-OR-NOT-structured-fields-severity-timestamp-trace-text",
|
|
349
350
|
"logs-list-delete",
|
|
350
351
|
"partialSuccess",
|
|
351
352
|
"explicit-official-Python-REST-SDK",
|
|
352
|
-
"setup-logging"
|
|
353
|
+
"setup-logging",
|
|
354
|
+
"owned-CloudRun-Functions-stdout-stderr",
|
|
355
|
+
"run-services-logs-read",
|
|
356
|
+
"functions-logs-read-gen1-gen2",
|
|
357
|
+
"SQLite-persistence-retention-redaction"
|
|
353
358
|
],
|
|
354
359
|
"limitations": [
|
|
355
|
-
"
|
|
356
|
-
"
|
|
357
|
-
"
|
|
358
|
-
"no-sinks-
|
|
360
|
+
"single-host-configurable-storage-capacity-default-2GiB-per-tenant",
|
|
361
|
+
"default-30-day-log-retention",
|
|
362
|
+
"bounded-durable-spool-and-Docker-log-rotation",
|
|
363
|
+
"no-sinks-alerts-or-native-Monitoring-Trace-API",
|
|
359
364
|
"no-new-IaC-resources"
|
|
360
365
|
]
|
|
361
366
|
},
|
|
@@ -378,16 +383,30 @@
|
|
|
378
383
|
"builds",
|
|
379
384
|
"gemini-models",
|
|
380
385
|
"vm-policies",
|
|
381
|
-
"eventarc-triggers"
|
|
386
|
+
"eventarc-triggers",
|
|
387
|
+
"gke-clusters",
|
|
388
|
+
"firewall-rules",
|
|
389
|
+
"health-checks",
|
|
390
|
+
"backend-services",
|
|
391
|
+
"url-maps",
|
|
392
|
+
"target-http-proxies",
|
|
393
|
+
"forwarding-rules",
|
|
394
|
+
"redis-instances"
|
|
382
395
|
],
|
|
383
396
|
"operations": [
|
|
384
397
|
"typed-crud",
|
|
385
398
|
"three-locale-mobile-console",
|
|
386
|
-
"fufu-infrastructure-cli"
|
|
399
|
+
"fufu-infrastructure-cli",
|
|
400
|
+
"native-gcloud-command-subsets",
|
|
401
|
+
"same-tenant-reference-validation",
|
|
402
|
+
"referenced-resource-delete-conflict"
|
|
387
403
|
],
|
|
388
404
|
"limitations": [
|
|
389
405
|
"configuration-only",
|
|
390
|
-
"no-VM-SQL-network-DNS-build-registry-inference-runtime",
|
|
406
|
+
"no-VM-SQL-network-DNS-build-registry-inference-Kubernetes-Redis-LB-runtime",
|
|
407
|
+
"configuration-envelope-not-official-resource-JSON",
|
|
408
|
+
"LB-global-subset-regional-flags-rejected",
|
|
409
|
+
"no-complete-reference-use-permission-model",
|
|
391
410
|
"no-new-IaC-resources"
|
|
392
411
|
]
|
|
393
412
|
},
|
|
@@ -492,8 +511,11 @@
|
|
|
492
511
|
},
|
|
493
512
|
"gcp-memorystore": {
|
|
494
513
|
"name": "Memorystore",
|
|
495
|
-
"status": "
|
|
496
|
-
"
|
|
514
|
+
"status": "mock-only",
|
|
515
|
+
"backend": "api",
|
|
516
|
+
"mode": "mock-only",
|
|
517
|
+
"operations": ["typed-redis-configuration-crud", "network-reference-validation"],
|
|
518
|
+
"reason": "僅保存 Memorystore 配置;不建立 Redis process、endpoint、replication 或 persistence。工作區既有 Redis 引擎是獨立服務。"
|
|
497
519
|
},
|
|
498
520
|
"aws-elasticache": {
|
|
499
521
|
"name": "ElastiCache",
|
|
@@ -512,18 +534,31 @@
|
|
|
512
534
|
},
|
|
513
535
|
"gcp-compute-engine": {
|
|
514
536
|
"name": "Compute Engine",
|
|
515
|
-
"status": "
|
|
537
|
+
"status": "implemented",
|
|
516
538
|
"backend": "api",
|
|
517
|
-
"mode": "
|
|
539
|
+
"mode": "simplified",
|
|
518
540
|
"operations": [
|
|
519
|
-
"
|
|
541
|
+
"create-list-describe-delete", "stop-start", "guest-ssh-command"
|
|
520
542
|
],
|
|
521
|
-
"
|
|
543
|
+
"limitations": ["requires-FUFU-infrastructure-backend", "Debian-12-only", "private-network", "single-node-disk", "no-self-service-GPU-passthrough", "no-official-IaC-provider"],
|
|
544
|
+
"reason": "實際 KVM/KubeVirt VM、持久磁碟與 SSH 指令;原生風格指令子集,無官方雲端資源。"
|
|
522
545
|
},
|
|
523
546
|
"gcp-gke": {
|
|
524
547
|
"name": "GKE",
|
|
525
|
-
"status": "
|
|
526
|
-
"
|
|
548
|
+
"status": "implemented",
|
|
549
|
+
"backend": "api",
|
|
550
|
+
"mode": "simplified",
|
|
551
|
+
"operations": ["create-list-describe-delete", "get-credentials", "kubectl-workloads", "GPU-container-jobs"],
|
|
552
|
+
"limitations": ["requires-FUFU-infrastructure-backend", "shared-workers", "single-control-plane-replica", "no-workload-PVC", "no-public-LoadBalancer", "no-interactive-exec-or-portforward", "no-official-IaC-provider"],
|
|
553
|
+
"reason": "獨立 vCluster Kubernetes API 與控制面資料;共用受管節點,支援標準 kubectl 工作負載子集。"
|
|
554
|
+
},
|
|
555
|
+
"gcp-vpc": {
|
|
556
|
+
"name": "VPC / Firewall",
|
|
557
|
+
"status": "mock-only",
|
|
558
|
+
"backend": "api",
|
|
559
|
+
"mode": "mock-only",
|
|
560
|
+
"operations": ["network-subnet-connector-firewall-configuration-crud"],
|
|
561
|
+
"reason": "僅保存配置;規則不會過濾封包,沒有路由、位址配置或真實 VPC。"
|
|
527
562
|
},
|
|
528
563
|
"gcp-cloud-run-jobs": {
|
|
529
564
|
"name": "Cloud Run Jobs",
|
|
@@ -592,17 +627,24 @@
|
|
|
592
627
|
"gcp-cloud-logging-monitoring": {
|
|
593
628
|
"name": "Cloud Logging / Monitoring",
|
|
594
629
|
"status": "partial",
|
|
595
|
-
"reason": "
|
|
630
|
+
"reason": "Cloud Logging 已接持久化、工作負載 stdout/stderr、CLI 與 UI;請求 metrics/traces 為明示 FUFU 擴充。未實作官方 Monitoring/Cloud Trace API、alerts 或 sinks。"
|
|
596
631
|
},
|
|
597
632
|
"gcp-load-balancing-nat": {
|
|
598
633
|
"name": "Cloud Load Balancing / NAT",
|
|
599
|
-
"status": "
|
|
600
|
-
"
|
|
634
|
+
"status": "mock-only",
|
|
635
|
+
"backend": "api",
|
|
636
|
+
"mode": "mock-only",
|
|
637
|
+
"operations": ["global-health-check-backend-url-map-http-proxy-forwarding-rule-configuration-crud"],
|
|
638
|
+
"reason": "LB 僅保存 global 配置相依鏈;沒有探測、IP、listener、TLS 或轉送。NAT 尚未支援。"
|
|
601
639
|
},
|
|
602
640
|
"aws-ec2": {
|
|
603
641
|
"name": "EC2",
|
|
604
|
-
"status": "
|
|
605
|
-
"
|
|
642
|
+
"status": "implemented",
|
|
643
|
+
"backend": "api",
|
|
644
|
+
"mode": "simplified",
|
|
645
|
+
"operations": ["run-describe-terminate-instances", "stop-start", "instance-waiters"],
|
|
646
|
+
"limitations": ["requires-FUFU-infrastructure-backend", "Debian-12-only", "private-network", "single-node-disk", "no-self-service-GPU-passthrough", "no-official-IaC-provider"],
|
|
647
|
+
"reason": "實際 KVM/KubeVirt VM;支援生命週期與 scoped instance ID,無官方 EC2 網路或完整 API。"
|
|
606
648
|
},
|
|
607
649
|
"aws-ecs": {
|
|
608
650
|
"name": "ECS / Fargate",
|
|
@@ -611,8 +653,12 @@
|
|
|
611
653
|
},
|
|
612
654
|
"aws-eks": {
|
|
613
655
|
"name": "EKS",
|
|
614
|
-
"status": "
|
|
615
|
-
"
|
|
656
|
+
"status": "implemented",
|
|
657
|
+
"backend": "api",
|
|
658
|
+
"mode": "simplified",
|
|
659
|
+
"operations": ["create-list-describe-delete", "update-kubeconfig", "cluster-waiters", "kubectl-workloads", "GPU-container-jobs"],
|
|
660
|
+
"limitations": ["requires-FUFU-infrastructure-backend", "shared-workers", "single-control-plane-replica", "no-workload-PVC", "no-public-LoadBalancer", "no-interactive-exec-or-portforward", "no-official-IaC-provider"],
|
|
661
|
+
"reason": "獨立 vCluster Kubernetes API 與控制面資料;FUFU 短效認證,不是 AWS 原生 IAM 或託管控制面。"
|
|
616
662
|
},
|
|
617
663
|
"aws-app-runner": {
|
|
618
664
|
"name": "App Runner",
|
|
@@ -664,8 +710,8 @@
|
|
|
664
710
|
},
|
|
665
711
|
"aws-cloudwatch": {
|
|
666
712
|
"name": "CloudWatch",
|
|
667
|
-
"status": "
|
|
668
|
-
"reason": "
|
|
713
|
+
"status": "partial",
|
|
714
|
+
"reason": "CloudWatch Logs 明列 API 子集、Lambda stdout/stderr、CLI tail、持久化與 retention 已接入;請求 metrics/traces 使用 FUFU 擴充,未實作官方 Metrics/X-Ray API 或 alarms。"
|
|
669
715
|
},
|
|
670
716
|
"aws-cloudfront": {
|
|
671
717
|
"name": "CloudFront",
|
|
@@ -104,6 +104,7 @@ def fufu_parser():
|
|
|
104
104
|
p = Parser(prog="fufu", description="FUFU 租戶認證、遠端部署與本機 Docker;所有輸出均為 FUFU 子集契約。")
|
|
105
105
|
p.add_argument("--version", action="version", version=__version__)
|
|
106
106
|
sub = p.add_subparsers(dest="action", required=True)
|
|
107
|
+
sub.add_parser('observability', help='FUFU 擴充:日誌、請求指標、trace 與收集狀態;使用 observability --help')
|
|
107
108
|
auth = sub.add_parser("auth", help="驗證 CLI credential,顯示目前租戶與 scope").add_subparsers(dest="auth_action", required=True)
|
|
108
109
|
identity = auth.add_parser("whoami")
|
|
109
110
|
identity.add_argument("--provider", choices=["aws", "gcp", "azure", "cloudflare"])
|
|
@@ -495,7 +496,7 @@ def entry(provider, argv=None):
|
|
|
495
496
|
p.add_argument('--quiet', '-q', action='store_true', help='非互動執行')
|
|
496
497
|
p.add_argument('--version', action='store_true', help='CLI 版本')
|
|
497
498
|
groups = p.add_subparsers(title='服務')
|
|
498
|
-
for group in ['run', 'functions', 'iam', 'projects', 'pubsub', 'secrets', 'storage', 'auth', 'scheduler', 'tasks', 'logging', 'compute', 'sql', 'artifacts', 'dns', 'eventarc']: groups.add_parser(group)
|
|
499
|
+
for group in ['run', 'functions', 'iam', 'projects', 'pubsub', 'secrets', 'storage', 'auth', 'scheduler', 'tasks', 'logging', 'compute', 'sql', 'artifacts', 'dns', 'eventarc', 'container', 'redis']: groups.add_parser(group)
|
|
499
500
|
p.print_help(); return
|
|
500
501
|
if provider == 'bq-fufu':
|
|
501
502
|
from .bigquery import command
|
|
@@ -517,12 +518,20 @@ def entry(provider, argv=None):
|
|
|
517
518
|
if provider == 'fufu' and argv[:1] == ['infrastructure']:
|
|
518
519
|
from .gcp_infra import configuration_command
|
|
519
520
|
emit(configuration_command(argv[1:], Parser)); return
|
|
521
|
+
if provider == 'fufu' and argv[:1] == ['kubernetes-token']:
|
|
522
|
+
from .infrastructure import token_command
|
|
523
|
+
emit(token_command(argv[1:], Parser)); return
|
|
520
524
|
# Native global selectors are checked against the authenticated tenant scope.
|
|
521
525
|
if provider in {'aws-fufu', 'gcloud-fufu'}:
|
|
522
|
-
rest = []; i = 0
|
|
526
|
+
rest = []; i = 0; explicit_region = False
|
|
527
|
+
native_tail=provider=='aws-fufu' and any(argv[j:j+2]==['logs','tail'] for j in range(len(argv)))
|
|
528
|
+
native_s3=provider=='aws-fufu' and 's3' in argv
|
|
529
|
+
native_storage_ls=provider=='gcloud-fufu' and any(argv[j:j+2]==['storage','ls'] for j in range(len(argv)))
|
|
530
|
+
native_function_logs=provider=='gcloud-fufu' and any(argv[j:j+3]==['functions','logs','read'] for j in range(len(argv)))
|
|
523
531
|
while i < len(argv):
|
|
524
532
|
flag, _, inline = argv[i].partition('=')
|
|
525
|
-
|
|
533
|
+
native_option=flag=='--format' and (native_tail or native_storage_ls) or flag=='--filter' and native_function_logs
|
|
534
|
+
if not native_option and (flag in {'--region', '--project', '--output', '--format', '--impersonate-service-account', '--filter', '--sort-by', '--query'} or flag == '--limit' and provider == 'gcloud-fufu' and 'list' in argv):
|
|
526
535
|
value = inline if '=' in argv[i] else (argv[i + 1] if i + 1 < len(argv) else '')
|
|
527
536
|
i += 1 if '=' in argv[i] else 2
|
|
528
537
|
require(bool(value) and not value.startswith('--'), 'UNSUPPORTED_ARGUMENT', '全域旗標缺少值。')
|
|
@@ -531,7 +540,7 @@ def entry(provider, argv=None):
|
|
|
531
540
|
from .aws_output import expression
|
|
532
541
|
aws_query = expression(value)
|
|
533
542
|
elif flag == '--output' and provider == 'aws-fufu':
|
|
534
|
-
require(value in {'json', 'text', 'yaml', 'off'}, 'UNSUPPORTED_FORMAT', 'AWS 輸出支援 json、text、yaml、off。')
|
|
543
|
+
require(value in ({'json', 'text', 'yaml', 'table', 'yaml-stream', 'off'} if native_s3 else {'json', 'text', 'yaml', 'off'}), 'UNSUPPORTED_FORMAT', 'AWS 輸出支援 json、text、yaml、off。')
|
|
535
544
|
aws_format = value
|
|
536
545
|
elif flag == '--impersonate-service-account':
|
|
537
546
|
require(provider == 'gcloud-fufu', 'UNSUPPORTED_ARGUMENT', '此旗標只適用於 gcloud-fufu。')
|
|
@@ -550,12 +559,16 @@ def entry(provider, argv=None):
|
|
|
550
559
|
if flag == '--limit':
|
|
551
560
|
require(value.isdigit(), 'UNSUPPORTED_ARGUMENT', 'limit 必須為非負整數。'); output_limit = int(value)
|
|
552
561
|
elif flag in {'--output', '--format'}: require(value == 'json', 'UNSUPPORTED_ARGUMENT', '此入口目前輸出格式支援 json。')
|
|
553
|
-
else:
|
|
554
|
-
|
|
562
|
+
else:
|
|
563
|
+
if flag == '--region': explicit_region = True
|
|
564
|
+
os.environ['FUFU_SELECTED_REGION' if flag == '--region' else 'FUFU_SELECTED_PROJECT'] = value
|
|
565
|
+
elif flag in {'--quiet', '-q', '--no-cli-pager'} and not (native_s3 and flag == '--quiet'):
|
|
555
566
|
require('=' not in argv[i], 'UNSUPPORTED_ARGUMENT', '布林旗標不接受額外值。'); i += 1
|
|
556
567
|
else: rest.append(argv[i]); i += 1
|
|
557
568
|
argv = rest
|
|
558
569
|
if provider == 'gcloud-fufu':
|
|
570
|
+
if explicit_region and argv[:2] in [['compute', name] for name in ['health-checks', 'backend-services', 'url-maps', 'target-http-proxies', 'forwarding-rules']]:
|
|
571
|
+
require(False, 'CAPABILITY_NOT_SUPPORTED', '目前 LB 配置僅支援 global 子集;不能以 --region 建立或查詢區域資源。')
|
|
559
572
|
if output_filter or output_sort or output_limit is not None:
|
|
560
573
|
list_commands = [('run', 'services', 'list'), ('functions', 'list'), ('iam', 'service-accounts', 'list'),
|
|
561
574
|
('pubsub', 'topics', 'list'), ('pubsub', 'subscriptions', 'list'), ('secrets', 'list'),
|
|
@@ -564,11 +577,19 @@ def entry(provider, argv=None):
|
|
|
564
577
|
('compute', 'instances', 'list'), ('sql', 'instances', 'list'), ('sql', 'databases', 'list'),
|
|
565
578
|
('artifacts', 'repositories', 'list'), ('dns', 'managed-zones', 'list'), ('eventarc', 'triggers', 'list'),
|
|
566
579
|
('compute', 'networks', 'list'), ('compute', 'networks', 'subnets', 'list'), ('compute', 'networks', 'vpc-access', 'connectors', 'list')]
|
|
580
|
+
from .gcp_infra import MOCK_GROUPS
|
|
581
|
+
list_commands.extend((*command, 'list') for command in MOCK_GROUPS)
|
|
567
582
|
require(any(tuple(argv[:len(command)]) == command for command in list_commands), 'UNSUPPORTED_ARGUMENT', '清單選取旗標只適用於資源 list;未執行操作。')
|
|
568
583
|
if output_format and argv[:2] == ['functions', 'call']:
|
|
569
584
|
require(output_format == 'json', 'UNSUPPORTED_FORMAT', 'Functions call 目前只支援 JSON 回應。')
|
|
570
585
|
from . import public_access
|
|
571
|
-
if provider == 'gcloud-fufu' and argv
|
|
586
|
+
if provider == 'gcloud-fufu' and argv[:2] in [['compute', 'instances'], ['compute', 'ssh'], ['container', 'clusters']]:
|
|
587
|
+
from .infrastructure import gcp_command
|
|
588
|
+
emit(gcp_command(argv, Parser)); return
|
|
589
|
+
if provider == 'aws-fufu' and argv[:1] in [['ec2'], ['eks']]:
|
|
590
|
+
from .infrastructure import aws_command as infrastructure_aws_command
|
|
591
|
+
emit(infrastructure_aws_command(argv, Parser)); return
|
|
592
|
+
if provider == 'gcloud-fufu' and argv and argv[0] in {'scheduler', 'tasks', 'logging', 'compute', 'sql', 'artifacts', 'dns', 'eventarc', 'container', 'redis'}:
|
|
572
593
|
from .gcp_infra import command
|
|
573
594
|
emit(command(argv, Parser)); return
|
|
574
595
|
if provider == 'aws-fufu' and argv[:1] == ['secretsmanager'] or provider == 'gcloud-fufu' and argv[:1] == ['secrets']:
|
|
@@ -588,6 +609,15 @@ def entry(provider, argv=None):
|
|
|
588
609
|
if provider == 'aws-fufu' and argv and argv[0] == 'serverless':
|
|
589
610
|
from .serverless import command
|
|
590
611
|
emit(command(argv[1:], Parser)); return
|
|
612
|
+
if provider=='fufu' and argv and argv[0]=='observability':
|
|
613
|
+
from .observability import command
|
|
614
|
+
emit(command(argv[1:],Parser));return
|
|
615
|
+
if provider=='aws-fufu' and argv[:2]==['logs','tail']:
|
|
616
|
+
from .observability import aws_tail
|
|
617
|
+
aws_tail(argv[2:],Parser);return
|
|
618
|
+
if provider=='gcloud-fufu' and (argv[:4]==['run','services','logs','read'] or argv[:3]==['functions','logs','read']):
|
|
619
|
+
from .observability import service_logs
|
|
620
|
+
emit(service_logs(argv,Parser));return
|
|
591
621
|
if provider == 'fufu' and argv and argv[0] in {'gateway', 'events', 'mock'}:
|
|
592
622
|
from .control import fufu_control
|
|
593
623
|
emit(fufu_control(argv, Parser)); return
|
|
@@ -609,7 +639,10 @@ def entry(provider, argv=None):
|
|
|
609
639
|
if provider == 'az-fufu' and argv and argv[0] == 'functionapp':
|
|
610
640
|
raise FufuError('CAPABILITY_NOT_SUPPORTED', 'Azure Functions runtime 尚未實作。', 2)
|
|
611
641
|
remote = {"aws-fufu": "aws", "gcloud-fufu": "gcp", "az-fufu": "azure"}
|
|
612
|
-
if provider == 'aws-fufu' and argv
|
|
642
|
+
if provider == 'aws-fufu' and argv[:1] == ['s3']:
|
|
643
|
+
from .s3_highlevel import command
|
|
644
|
+
command(argv[1:], Parser); return
|
|
645
|
+
if provider == 'aws-fufu' and argv and argv[0] in {'sqs', 'ssm'}:
|
|
613
646
|
from .cloud_data import aws_data_command
|
|
614
647
|
emit(aws_data_command(argv, Parser))
|
|
615
648
|
return
|
|
@@ -28,7 +28,7 @@ def s3_path(value):
|
|
|
28
28
|
p = urlsplit(value)
|
|
29
29
|
require(p.scheme == 's3' and p.netloc and not p.query and not p.fragment and not p.username and not p.password,
|
|
30
30
|
'INVALID_RESOURCE', '請使用 s3://bucket 或 s3://bucket/key。')
|
|
31
|
-
return '/s3/buckets/' + segment(p.netloc), p.path.
|
|
31
|
+
return '/s3/buckets/' + segment(p.netloc), p.path.removeprefix('/')
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def aws_data_command(argv, Parser):
|
|
@@ -27,7 +27,7 @@ AWS_OPERATIONS = {
|
|
|
27
27
|
'BatchGetItem': 'RequestItems', 'BatchWriteItem': 'RequestItems',
|
|
28
28
|
},
|
|
29
29
|
's3api': {
|
|
30
|
-
'CreateBucket': 'Bucket CreateBucketConfiguration', 'DeleteBucket': 'Bucket', 'ListBuckets': '', 'HeadBucket': 'Bucket',
|
|
30
|
+
'CreateBucket': 'Bucket CreateBucketConfiguration', 'DeleteBucket': 'Bucket', 'ListBuckets': '', 'HeadBucket': 'Bucket', 'GetBucketLocation': 'Bucket',
|
|
31
31
|
'ListObjectsV2': 'Bucket Prefix MaxKeys ContinuationToken Delimiter StartAfter EncodingType', 'PutObject': 'Bucket Key Body ContentType Metadata',
|
|
32
32
|
'GetObject': 'Bucket Key ChecksumMode Range IfMatch IfNoneMatch', 'HeadObject': 'Bucket Key ChecksumMode IfMatch IfNoneMatch', 'DeleteObject': 'Bucket Key',
|
|
33
33
|
},
|
|
@@ -134,7 +134,7 @@ def command(argv, Parser):
|
|
|
134
134
|
params.setdefault('Type', 'String')
|
|
135
135
|
if a.service == 'sqs' and 'QueueUrl' in params:
|
|
136
136
|
require(isinstance(params['QueueUrl'], str) and re.fullmatch(r'(?:fufu://sqs/|https://sqs\.[a-z0-9-]+\.fufu\.invalid/[0-9]{12}/)[A-Za-z0-9_-]+(?:\.fifo)?', params['QueueUrl']) and len(params['QueueUrl'].rsplit('/', 1)[-1]) <= 80, 'INVALID_RESOURCE', 'QueueUrl 必須是 FUFU 租戶資源識別。')
|
|
137
|
-
with ApiClient('aws', read_only=bool(re.match(r'^(Get|List|Describe|Query|Scan|BatchGet|Simulate|Head)', a.native_operation))) as api:
|
|
137
|
+
with ApiClient('aws', read_only=bool(re.match(r'^(Get|List|Describe|Query|Scan|BatchGet|Simulate|Head|FilterLogEvents)', a.native_operation))) as api:
|
|
138
138
|
if a.service == 's3api' and a.native_operation == 'PutObject' and isinstance(params.get('Body'), Path):
|
|
139
139
|
from .object_transfer import upload_file
|
|
140
140
|
return upload_file(api, params['Body'], params.get('Bucket'), params.get('Key'), content_type=params.get('ContentType'), metadata=params.get('Metadata'))
|