fix(ci): publish valid worker images
Normalize registry and release tag references before Buildx runs. Use process-local liveness so a control-plane outage does not restart a healthy worker container.
Этот коммит содержится в:
24
.github/workflows/docker.yml
поставляемый
24
.github/workflows/docker.yml
поставляемый
@@ -26,28 +26,46 @@ jobs:
|
|||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Normalize Harbor registry
|
||||||
|
id: registry
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
REGISTRY="${HARBOR_REGISTRY,,}"
|
||||||
|
REGISTRY="${REGISTRY#http://}"
|
||||||
|
REGISTRY="${REGISTRY#https://}"
|
||||||
|
while [ "${REGISTRY%/}" != "${REGISTRY}" ]; do
|
||||||
|
REGISTRY="${REGISTRY%/}"
|
||||||
|
done
|
||||||
|
if [[ ! "${REGISTRY}" =~ ^[a-z0-9.-]+(:[0-9]+)?$ ]]; then
|
||||||
|
echo "HARBOR_REGISTRY must contain only a registry host and optional port" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "host=${REGISTRY}" >> "${GITHUB_OUTPUT}"
|
||||||
|
|
||||||
- name: Log in to Harbor
|
- name: Log in to Harbor
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.HARBOR_REGISTRY }}
|
registry: ${{ steps.registry.outputs.host }}
|
||||||
username: ${{ secrets.HARBOR_USER }}
|
username: ${{ secrets.HARBOR_USER }}
|
||||||
password: ${{ secrets.HARBOR_PASSWORD }}
|
password: ${{ secrets.HARBOR_PASSWORD }}
|
||||||
|
|
||||||
- name: Compute image metadata
|
- name: Compute image metadata
|
||||||
id: vars
|
id: vars
|
||||||
env:
|
env:
|
||||||
|
REGISTRY: ${{ steps.registry.outputs.host }}
|
||||||
REF_NAME: ${{ gitea.ref_name }}
|
REF_NAME: ${{ gitea.ref_name }}
|
||||||
REF_TYPE: ${{ gitea.ref_type }}
|
REF_TYPE: ${{ gitea.ref_type }}
|
||||||
SHA: ${{ gitea.sha }}
|
SHA: ${{ gitea.sha }}
|
||||||
run: |
|
run: |
|
||||||
IMAGE="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}"
|
IMAGE="${REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}"
|
||||||
SHORT_SHA="$(printf '%s' "${SHA}" | cut -c1-12)"
|
SHORT_SHA="$(printf '%s' "${SHA}" | cut -c1-12)"
|
||||||
TAGS="${IMAGE}:sha-${SHORT_SHA}"
|
TAGS="${IMAGE}:sha-${SHORT_SHA}"
|
||||||
if [ "${REF_TYPE}" = "branch" ] && [ "${REF_NAME}" = "master" ]; then
|
if [ "${REF_TYPE}" = "branch" ] && [ "${REF_NAME}" = "master" ]; then
|
||||||
TAGS="${TAGS},${IMAGE}:latest"
|
TAGS="${TAGS},${IMAGE}:latest"
|
||||||
fi
|
fi
|
||||||
if [ "${REF_TYPE}" = "tag" ]; then
|
if [ "${REF_TYPE}" = "tag" ]; then
|
||||||
TAGS="${TAGS},${IMAGE}:${REF_NAME}"
|
RELEASE_TAG="$(printf '%s' "${REF_NAME}" | tr -c 'A-Za-z0-9_.-' '-' | cut -c1-128)"
|
||||||
|
TAGS="${TAGS},${IMAGE}:${RELEASE_TAG}"
|
||||||
fi
|
fi
|
||||||
{
|
{
|
||||||
echo "image=${IMAGE}"
|
echo "image=${IMAGE}"
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ EXPOSE 27401 37401
|
|||||||
USER rsmon-worker
|
USER rsmon-worker
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
||||||
CMD ["/usr/local/bin/rsmon-worker", "health"]
|
CMD ["/usr/local/bin/rsmon-worker", "liveness"]
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/rsmon-worker"]
|
ENTRYPOINT ["/usr/local/bin/rsmon-worker"]
|
||||||
|
|||||||
1
Makefile
1
Makefile
@@ -12,6 +12,7 @@ build:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
RSMON_ENV=test CWD=$(CURDIR) go test \
|
RSMON_ENV=test CWD=$(CURDIR) go test \
|
||||||
|
./cmd/rsmon-worker \
|
||||||
./internal/distworker \
|
./internal/distworker \
|
||||||
./internal/webapp \
|
./internal/webapp \
|
||||||
./internal/workercluster \
|
./internal/workercluster \
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ func main() {
|
|||||||
if len(flag.Args()) > 0 && flag.Arg(0) == "health" {
|
if len(flag.Args()) > 0 && flag.Arg(0) == "health" {
|
||||||
os.Exit(healthCheck())
|
os.Exit(healthCheck())
|
||||||
}
|
}
|
||||||
|
if len(flag.Args()) > 0 && flag.Arg(0) == "liveness" {
|
||||||
|
os.Exit(livenessCheck())
|
||||||
|
}
|
||||||
|
|
||||||
log.Println("rsmon-worker starting...")
|
log.Println("rsmon-worker starting...")
|
||||||
|
|
||||||
@@ -586,3 +589,32 @@ func healthCheck() int {
|
|||||||
fmt.Println("health check ok")
|
fmt.Println("health check ok")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func livenessCheck() int {
|
||||||
|
httpCfg := distworker.HTTPConfigFromEnv()
|
||||||
|
host := httpCfg.Host
|
||||||
|
switch host {
|
||||||
|
case "", "0.0.0.0":
|
||||||
|
host = "127.0.0.1"
|
||||||
|
case "::", "[::]":
|
||||||
|
host = "::1"
|
||||||
|
}
|
||||||
|
endpoint := "http://" + net.JoinHostPort(host, strconv.Itoa(httpCfg.Port)) + "/healthz"
|
||||||
|
return probeLiveness(endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func probeLiveness(endpoint string) int {
|
||||||
|
client := &http.Client{Timeout: 5 * time.Second}
|
||||||
|
resp, err := client.Get(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "liveness check failed: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
defer resp.Body.Close() //nolint:errcheck
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
fmt.Fprintf(os.Stderr, "liveness check failed: status %s\n", resp.Status)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fmt.Println("liveness check ok")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|||||||
33
cmd/rsmon-worker/main_test.go
Обычный файл
33
cmd/rsmon-worker/main_test.go
Обычный файл
@@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProbeLiveness(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
code int
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{name: "healthy", code: http.StatusOK, want: 0},
|
||||||
|
{name: "unhealthy", code: http.StatusServiceUnavailable, want: 1},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(tt.code)
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
if got := probeLiveness(srv.URL); got != tt.want {
|
||||||
|
t.Fatalf("probeLiveness() = %d, want %d", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Ссылка в новой задаче
Block a user