fix(source): add /services/{name}/source endpoint; fix 404 for service code view in funcs-console
This commit is contained in:
@@ -29,7 +29,7 @@ spec:
|
||||
- name: sless-registry-auth
|
||||
containers:
|
||||
- name: funcs
|
||||
image: pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-funcs-service:v0.2.1
|
||||
image: pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-funcs-service:v0.2.2
|
||||
ports:
|
||||
- containerPort: 8090
|
||||
env:
|
||||
|
||||
@@ -74,7 +74,7 @@ spec:
|
||||
containers:
|
||||
- name: operator
|
||||
# При обновлении версии оператора — менять тег здесь (не latest!)
|
||||
image: pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-operator:v0.1.44
|
||||
image: pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-operator:v0.1.45
|
||||
# Always — чтобы всегда тянуть по точному тегу (не кешировать старый)
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
|
||||
@@ -66,6 +66,43 @@ func (h *Handler) GetSource(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, files)
|
||||
}
|
||||
|
||||
// GetServiceSource — GET /v1/namespaces/{namespace}/services/{name}/source
|
||||
// Аналог GetSource для sless_service: скачивает tar.gz из S3 и возвращает пользовательские файлы.
|
||||
func (h *Handler) GetServiceSource(w http.ResponseWriter, r *http.Request) {
|
||||
ns := namespace(r)
|
||||
name := pathVar(r, "name")
|
||||
|
||||
svc := &slessv1alpha1.Service{}
|
||||
if err := h.K8s.Get(r.Context(), client.ObjectKey{Name: name, Namespace: ns}, svc); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
writeJSON(w, http.StatusNotFound, errResp("service not found"))
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusInternalServerError, errResp(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if svc.Spec.S3Key == "" {
|
||||
writeJSON(w, http.StatusOK, []sourceFileEntry{})
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := h.S3.Download(r.Context(), svc.Spec.S3Key)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, errResp("download from S3: "+err.Error()))
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
files, err := extractSourceFilesFromTarGz(rc)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, errResp("extract source: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, files)
|
||||
}
|
||||
|
||||
// extractSourceFilesFromTarGz читает tar.gz и возвращает пользовательские файлы.
|
||||
// Dockerfile пропускается — он генерируется builder-ом автоматически.
|
||||
// Бинарные файлы (содержат null bytes) возвращаются в base64 с binary=true.
|
||||
|
||||
@@ -55,6 +55,7 @@ func NewRouter(h *handler.Handler, log *slog.Logger) http.Handler {
|
||||
v1.HandleFunc("/namespaces/{namespace}/services/{name}", h.UpdateService).Methods(http.MethodPut)
|
||||
v1.HandleFunc("/namespaces/{namespace}/services/{name}", h.DeleteService).Methods(http.MethodDelete)
|
||||
v1.HandleFunc("/namespaces/{namespace}/services/{name}/upload", h.UploadServiceCode).Methods(http.MethodPost)
|
||||
v1.HandleFunc("/namespaces/{namespace}/services/{name}/source", h.GetServiceSource).Methods(http.MethodGet)
|
||||
|
||||
// Triggers CRUD
|
||||
v1.HandleFunc("/namespaces/{namespace}/triggers", h.ListTriggers).Methods(http.MethodGet)
|
||||
|
||||
@@ -416,7 +416,7 @@
|
||||
icon.style.transform = 'rotate(90deg)';
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
loadSource(body, ns, fn.name);
|
||||
loadSource(body, ns, fn.name, fn.kind);
|
||||
}
|
||||
} else {
|
||||
body.style.display = 'none';
|
||||
@@ -458,12 +458,13 @@
|
||||
return btn;
|
||||
}
|
||||
|
||||
function loadSource(body, ns, fnName) {
|
||||
function loadSource(body, ns, fnName, fnKind) {
|
||||
var msg = $el('div', 'src-msg');
|
||||
msg.textContent = 'Загрузка кода…';
|
||||
body.appendChild(msg);
|
||||
|
||||
fetch('/funcs/' + ns + '/source/' + fnName)
|
||||
var kindParam = fnKind === 'service' ? '?kind=service' : '';
|
||||
fetch('/funcs/' + ns + '/source/' + fnName + kindParam)
|
||||
.then(function (r) {
|
||||
body.removeChild(msg);
|
||||
if (!r.ok) {
|
||||
|
||||
@@ -347,14 +347,19 @@ func servePlainText(w http.ResponseWriter, ns, externalURL string, fns []fnRespo
|
||||
fmt.Fprint(w, sb.String())
|
||||
}
|
||||
|
||||
// proxySourceGet проксирует GET /funcs/{ns}/source/{fn} → оператор GET /v1/.../source.
|
||||
// proxySourceGet проксирует GET /funcs/{ns}/source/{fn}?kind={function|service} → оператор.
|
||||
// Авторизация — сервисный токен (хранится на сервере, не открывается клиенту).
|
||||
// kind=service → /v1/.../services/{fn}/source; иначе → /v1/.../functions/{fn}/source
|
||||
func proxySourceGet(w http.ResponseWriter, r *http.Request, operatorURL, serviceToken, ns, funcName string) {
|
||||
if !isValidK8sName(funcName) {
|
||||
http.Error(w, "invalid function name\n", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
target := operatorURL + "/v1/namespaces/" + ns + "/functions/" + funcName + "/source"
|
||||
resourceType := "functions"
|
||||
if r.URL.Query().Get("kind") == "service" {
|
||||
resourceType = "services"
|
||||
}
|
||||
target := operatorURL + "/v1/namespaces/" + ns + "/" + resourceType + "/" + funcName + "/source"
|
||||
req, err := http.NewRequestWithContext(r.Context(), http.MethodGet, target, nil)
|
||||
if err != nil {
|
||||
http.Error(w, "internal error\n", http.StatusInternalServerError)
|
||||
|
||||
Reference in New Issue
Block a user