api-gateway: migrate YAML gen + provider core from deck-api (?endpoint=) to lk-api-gateway (REST paths)

- service_spec_gen: callAPI() auto-detects proxy vs REST mode by index.cfm presence
- core/client.go: buildURL() replaces all 4 ?endpoint= harcoded sites
- build-provider.sh: -ldflags -X main.version=${VERSION} for correct binary version
- 01_generate_yamls.sh (devops + root): Python auto-detect proxy vs REST
- 02_generate_resources_and_docs_v2.sh: pass -api-endpoint to doc generators
- 04_build_and_publish_docs.sh: normalize_api_endpoint without forced /index.cfm
- docs_template_gen + v2: -api-endpoint flag, hardcoded deck-api replaced
- provider.go (both): TODO(api-gateway) comments
- profiles/test/profile.env: NUBES_API_ENDPOINT -> lk-api-gateway-test
- HISTORY: 2026-07-02_api_gateway_migration.md
This commit is contained in:
“Naeel”
2026-07-02 12:40:47 +04:00
parent 54647b2fbf
commit 612aa53caf
13 changed files with 212 additions and 68 deletions
@@ -27,6 +27,9 @@ import (
// Env:
// - NUBES_API_TOKEN (preferred) or TOKEN_FILE
// - NUBES_API_ENDPOINT (default: https://deck-api.ngcloud.ru/api/v1/index.cfm)
// Supports two modes, auto-detected:
// - Proxy (legacy): contains "index.cfm" → ?endpoint=/services/123
// - REST (new API Gateway): no "index.cfm" → /api/v1/svc/services/123
// - NUBES_SERVICE_ID (optional for single service)
// - NUBES_SERVICE_NAME (optional override for single service)
// - NUBES_SERVICES_FILE (default: devops/config/services_list.txt)
@@ -142,7 +145,8 @@ type serviceRef struct {
}
func loadConfig() (config, error) {
// API endpoint defaults to production; token is required.
// API endpoint defaults to production (legacy proxy); token is required.
// New API Gateway (REST) endpoints are also supported — auto-detected by presence of "index.cfm".
apiEndpoint := getenvDefault("NUBES_API_ENDPOINT", "https://deck-api.ngcloud.ru/api/v1/index.cfm")
apiEndpoint = normalizeAPIEndpoint(apiEndpoint)
apiToken, err := loadToken()
@@ -372,7 +376,7 @@ func (c *apiClient) getService(serviceID int) (serviceInfo, error) {
// /services/{id} returns display names, MAN, and a list of operations.
endpoint := fmt.Sprintf("/services/%d", serviceID)
var res serviceResponse
if err := c.getViaProxy(endpoint, &res); err != nil {
if err := c.callAPI(endpoint, &res); err != nil {
return serviceInfo{}, err
}
return res.Service, nil
@@ -382,13 +386,13 @@ func (c *apiClient) getServiceOperation(svcOperationId int) (serviceOperationInf
// /serviceOperation/{id} returns params and MAN for a single operation.
endpoint := fmt.Sprintf("/serviceOperation/%d", svcOperationId)
var res serviceOperationResponse
if err := c.getViaProxy(endpoint, &res); err != nil {
if err := c.callAPI(endpoint, &res); err != nil {
return serviceOperationInfo{}, err
}
return res.ServiceOperation, nil
}
func (c *apiClient) getViaProxy(endpoint string, out interface{}) error {
func (c *apiClient) callAPI(endpoint string, out interface{}) error {
const maxRetries = 3
baseDelay := 2 * time.Second
@@ -398,13 +402,18 @@ func (c *apiClient) getViaProxy(endpoint string, out interface{}) error {
time.Sleep(baseDelay * time.Duration(1<<(attempt-1)))
}
req, err := http.NewRequest("GET", c.endpoint, nil)
var reqURL string
if c.isProxyAPI() {
// Legacy proxy: index.cfm?endpoint=/services/123
reqURL = c.endpoint + "?endpoint=" + endpoint
} else {
// New REST gateway: /api/v1/svc/services/123
reqURL = c.endpoint + endpoint
}
req, err := http.NewRequest("GET", reqURL, nil)
if err != nil {
return err
}
q := req.URL.Query()
q.Set("endpoint", endpoint)
req.URL.RawQuery = q.Encode()
if c.token != "" {
req.Header.Set("Authorization", "Bearer "+c.token)
}
@@ -441,7 +450,7 @@ func (c *apiClient) getViaProxy(endpoint string, out interface{}) error {
return json.Unmarshal(body, out)
}
return fmt.Errorf("getViaProxy failed after %d retries: %w", maxRetries, lastErr)
return fmt.Errorf("callAPI failed after %d retries: %w", maxRetries, lastErr)
}
func (c *apiClient) collectOperations(ops []operationInfo) ([]OperationSpec, bool, bool, bool, error) {
@@ -725,13 +734,13 @@ func collapseUnderscores(value string) string {
}
func normalizeAPIEndpoint(raw string) string {
// Accept base API URL with or without index.cfm; normalize to index.cfm.
endpoint := strings.TrimSpace(raw)
if endpoint == "" {
return endpoint
}
if strings.HasSuffix(endpoint, "/index.cfm") {
return endpoint
}
return strings.TrimRight(endpoint, "/") + "/index.cfm"
// Accept both legacy proxy (index.cfm) and new REST gateway endpoints.
// Just trim trailing slash — no forced /index.cfm appending.
// The callAPI method auto-detects proxy vs REST mode by checking for "index.cfm".
return strings.TrimRight(strings.TrimSpace(raw), "/")
}
// isProxyAPI returns true if the endpoint uses the legacy ?endpoint= proxy pattern.
func (c *apiClient) isProxyAPI() bool {
return strings.Contains(c.endpoint, "index.cfm")
}