Add --add-domain, --remove-domain, --set-domains flags to C and Python CLIs
Post-creation custom domain management for services. All three flags
require --domains with a comma-separated list. Calls PUT /services/{id}/domains
with add, remove, or custom_domains action. CLI_SPEC.md updated.
This commit is contained in:
parent
2ae90fce12
commit
cf07b22a72
3 changed files with 208 additions and 0 deletions
|
|
@ -140,6 +140,9 @@ un service --unfreeze ID # Resume service
|
|||
un service --destroy ID # Delete service
|
||||
un service --lock ID # Prevent deletion
|
||||
un service --unlock ID # Allow deletion
|
||||
un service --add-domain ID --domains example.com # Add domains
|
||||
un service --remove-domain ID --domains example.com # Remove domains
|
||||
un service --set-domains ID --domains a.com,b.com # Replace all domains
|
||||
un service --execute ID 'cmd' # Run command in service
|
||||
un service --redeploy ID # Re-run bootstrap
|
||||
un service --snapshot ID # Create snapshot
|
||||
|
|
@ -165,6 +168,9 @@ un service --snapshot ID # Create snapshot
|
|||
| `--destroy ID` | Delete service |
|
||||
| `--lock ID` | Prevent deletion |
|
||||
| `--unlock ID` | Allow deletion |
|
||||
| `--add-domain ID` | Add domains (requires --domains) |
|
||||
| `--remove-domain ID` | Remove domains (requires --domains) |
|
||||
| `--set-domains ID` | Replace all domains (requires --domains) |
|
||||
| `--auto-unfreeze ID` | Enable auto-unfreeze on HTTP request |
|
||||
| `--no-auto-unfreeze ID` | Disable auto-unfreeze |
|
||||
| `--show-freeze-page ID` | Show payment page when frozen (default) |
|
||||
|
|
|
|||
|
|
@ -3766,6 +3766,115 @@ static int unlock_service(const UnsandboxCredentials *creds, const char *service
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Update custom domains for a service
|
||||
// action: "custom_domains" (full replace), "add", or "remove"
|
||||
// domains: comma-separated domain list
|
||||
static int update_service_domains(const UnsandboxCredentials *creds, const char *service_id, const char *action, const char *domains) {
|
||||
CURL *curl = curl_easy_init();
|
||||
if (!curl) return 1;
|
||||
|
||||
struct ResponseBuffer response = {0};
|
||||
response.data = malloc(1);
|
||||
response.size = 0;
|
||||
|
||||
char url[512];
|
||||
snprintf(url, sizeof(url), "%s/services/%s/domains", API_BASE, service_id);
|
||||
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "/services/%s/domains", service_id);
|
||||
|
||||
// Build JSON payload: {"action": ["domain1", "domain2"]}
|
||||
size_t payload_size = strlen(domains) * 2 + 256;
|
||||
char *payload = malloc(payload_size);
|
||||
if (!payload) {
|
||||
curl_easy_cleanup(curl);
|
||||
return 1;
|
||||
}
|
||||
char *p = payload;
|
||||
p += sprintf(p, "{\"%s\":[", action);
|
||||
|
||||
char *domains_copy = strdup(domains);
|
||||
char *token = strtok(domains_copy, ",");
|
||||
int first = 1;
|
||||
while (token) {
|
||||
while (*token == ' ') token++;
|
||||
char *end = token + strlen(token) - 1;
|
||||
while (end > token && *end == ' ') *end-- = '\0';
|
||||
if (!first) p += sprintf(p, ",");
|
||||
char *esc = escape_json_string(token);
|
||||
p += sprintf(p, "\"%s\"", esc);
|
||||
free(esc);
|
||||
first = 0;
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
free(domains_copy);
|
||||
p += sprintf(p, "]}");
|
||||
|
||||
struct curl_slist *headers = NULL;
|
||||
headers = add_hmac_auth_headers(headers, creds, "PUT", path, payload);
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
long http_code = 0;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
free(payload);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
fprintf(stderr, "Error: %s\n", curl_easy_strerror(res));
|
||||
free(response.data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (http_code == 404) {
|
||||
fprintf(stderr, "Error: Service not found\n");
|
||||
free(response.data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (http_code == 403) {
|
||||
fprintf(stderr, "Error: Not authorized to modify this service\n");
|
||||
free(response.data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (http_code != 200) {
|
||||
fprintf(stderr, "Error: HTTP %ld\n", http_code);
|
||||
if (response.data) fprintf(stderr, "%s\n", response.data);
|
||||
free(response.data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("\033[32mCustom domains updated successfully\033[0m\n");
|
||||
// Print resulting domains from response
|
||||
if (response.data) {
|
||||
// Quick parse for custom_domains array
|
||||
char *cd = strstr(response.data, "\"custom_domains\"");
|
||||
if (cd) {
|
||||
char *start = strchr(cd, '[');
|
||||
char *end = start ? strchr(start, ']') : NULL;
|
||||
if (start && end) {
|
||||
char tmp = *(end + 1);
|
||||
*(end + 1) = '\0';
|
||||
printf("Domains: %s\n", start);
|
||||
*(end + 1) = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(response.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set unfreeze_on_demand for a service
|
||||
static int set_unfreeze_on_demand(const UnsandboxCredentials *creds, const char *service_id, int enabled) {
|
||||
CURL *curl = curl_easy_init();
|
||||
|
|
@ -9735,6 +9844,9 @@ int main(int argc, char *argv[]) {
|
|||
const char *restore_snapshot_id = NULL;
|
||||
const char *snapshot_name = NULL;
|
||||
int hot_snapshot = 0;
|
||||
int do_add_domain = 0;
|
||||
int do_remove_domain = 0;
|
||||
int do_set_domains = 0;
|
||||
int create_unfreeze_on_demand = 0; // For --unfreeze-on-demand flag on create
|
||||
|
||||
// Environment variables for service create
|
||||
|
|
@ -9877,6 +9989,18 @@ int main(int argc, char *argv[]) {
|
|||
do_unlock = 1;
|
||||
i++;
|
||||
service_id = argv[i];
|
||||
} else if (strcmp(argv[i], "--add-domain") == 0 && i + 1 < argc) {
|
||||
do_add_domain = 1;
|
||||
i++;
|
||||
service_id = argv[i];
|
||||
} else if (strcmp(argv[i], "--remove-domain") == 0 && i + 1 < argc) {
|
||||
do_remove_domain = 1;
|
||||
i++;
|
||||
service_id = argv[i];
|
||||
} else if (strcmp(argv[i], "--set-domains") == 0 && i + 1 < argc) {
|
||||
do_set_domains = 1;
|
||||
i++;
|
||||
service_id = argv[i];
|
||||
} else if (strcmp(argv[i], "--auto-unfreeze") == 0 && i + 1 < argc) {
|
||||
do_auto_unfreeze = 1;
|
||||
i++;
|
||||
|
|
@ -10086,6 +10210,30 @@ int main(int argc, char *argv[]) {
|
|||
ret = lock_service(creds, service_id);
|
||||
} else if (do_unlock) {
|
||||
ret = unlock_service(creds, service_id);
|
||||
} else if (do_add_domain) {
|
||||
if (!service_domains) {
|
||||
fprintf(stderr, "Error: --domains required with --add-domain\n");
|
||||
curl_global_cleanup();
|
||||
free_credentials(creds);
|
||||
return 2;
|
||||
}
|
||||
ret = update_service_domains(creds, service_id, "add", service_domains);
|
||||
} else if (do_remove_domain) {
|
||||
if (!service_domains) {
|
||||
fprintf(stderr, "Error: --domains required with --remove-domain\n");
|
||||
curl_global_cleanup();
|
||||
free_credentials(creds);
|
||||
return 2;
|
||||
}
|
||||
ret = update_service_domains(creds, service_id, "remove", service_domains);
|
||||
} else if (do_set_domains) {
|
||||
if (!service_domains) {
|
||||
fprintf(stderr, "Error: --domains required with --set-domains\n");
|
||||
curl_global_cleanup();
|
||||
free_credentials(creds);
|
||||
return 2;
|
||||
}
|
||||
ret = update_service_domains(creds, service_id, "custom_domains", service_domains);
|
||||
} else if (do_auto_unfreeze) {
|
||||
ret = set_unfreeze_on_demand(creds, service_id, 1);
|
||||
} else if (do_no_auto_unfreeze) {
|
||||
|
|
|
|||
|
|
@ -1305,6 +1305,30 @@ def unlock_service(
|
|||
return _make_request("POST", f"/services/{service_id}/unlock", public_key, secret_key, {})
|
||||
|
||||
|
||||
def update_service_domains(
|
||||
service_id: str,
|
||||
domains: list,
|
||||
action: str = "custom_domains",
|
||||
public_key: Optional[str] = None,
|
||||
secret_key: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Update custom domains for a service.
|
||||
|
||||
Args:
|
||||
service_id: Service ID
|
||||
domains: List of domain strings
|
||||
action: "custom_domains" (replace), "add" (merge), or "remove" (subtract)
|
||||
public_key: Optional API key
|
||||
secret_key: Optional API secret
|
||||
|
||||
Returns:
|
||||
Response dict with updated domains
|
||||
"""
|
||||
public_key, secret_key = _resolve_credentials(public_key, secret_key)
|
||||
return _make_request("PUT", f"/services/{service_id}/domains", public_key, secret_key, {action: domains})
|
||||
|
||||
|
||||
def set_unfreeze_on_demand(
|
||||
service_id: str,
|
||||
enabled: bool,
|
||||
|
|
@ -2362,6 +2386,12 @@ Examples:
|
|||
help="Prevent deletion")
|
||||
service_group.add_argument("--unlock", metavar="ID",
|
||||
help="Allow deletion")
|
||||
service_group.add_argument("--add-domain", metavar="ID",
|
||||
help="Add domains (with --domains)")
|
||||
service_group.add_argument("--remove-domain", metavar="ID",
|
||||
help="Remove domains (with --domains)")
|
||||
service_group.add_argument("--set-domains", metavar="ID",
|
||||
help="Replace all domains (with --domains)")
|
||||
service_group.add_argument("--resize", metavar="ID",
|
||||
help="Resize service (with --vcpu)")
|
||||
service_group.add_argument("--redeploy", metavar="ID",
|
||||
|
|
@ -2667,6 +2697,30 @@ def _handle_service_command(args, public_key: str, secret_key: str):
|
|||
elif args.unlock:
|
||||
result = unlock_service(args.unlock, public_key, secret_key)
|
||||
print(f"Service {args.unlock} unlocked")
|
||||
elif getattr(args, 'add_domain', None):
|
||||
if not args.domains:
|
||||
print("Error: --domains required with --add-domain", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
domains = [d.strip() for d in args.domains.split(",")]
|
||||
result = update_service_domains(args.add_domain, domains, "add", public_key, secret_key)
|
||||
print(f"Domains added to {args.add_domain}")
|
||||
print(f"Domains: {result.get('custom_domains', [])}")
|
||||
elif getattr(args, 'remove_domain', None):
|
||||
if not args.domains:
|
||||
print("Error: --domains required with --remove-domain", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
domains = [d.strip() for d in args.domains.split(",")]
|
||||
result = update_service_domains(args.remove_domain, domains, "remove", public_key, secret_key)
|
||||
print(f"Domains removed from {args.remove_domain}")
|
||||
print(f"Domains: {result.get('custom_domains', [])}")
|
||||
elif getattr(args, 'set_domains', None):
|
||||
if not args.domains:
|
||||
print("Error: --domains required with --set-domains", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
domains = [d.strip() for d in args.domains.split(",")]
|
||||
result = update_service_domains(args.set_domains, domains, "custom_domains", public_key, secret_key)
|
||||
print(f"Domains set for {args.set_domains}")
|
||||
print(f"Domains: {result.get('custom_domains', [])}")
|
||||
elif args.resize:
|
||||
vcpu = getattr(args, 'vcpu', 1) or 1
|
||||
result = update_service(args.resize, public_key, secret_key, vcpu=vcpu)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue