mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-22 09:51:01 +00:00
Store agent ID in config file (#1888)
This commit is contained in:
parent
0e25d6d35a
commit
eb5c48a85f
8 changed files with 145 additions and 2 deletions
|
@ -23,6 +23,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -49,6 +50,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func run(c *cli.Context) error {
|
func run(c *cli.Context) error {
|
||||||
|
agentIDConfigPath := c.String("agent-id-config-path")
|
||||||
hostname := c.String("hostname")
|
hostname := c.String("hostname")
|
||||||
if len(hostname) == 0 {
|
if len(hostname) == 0 {
|
||||||
hostname, _ = os.Hostname()
|
hostname, _ = os.Hostname()
|
||||||
|
@ -109,7 +111,7 @@ func run(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
defer authConn.Close()
|
defer authConn.Close()
|
||||||
|
|
||||||
agentID := int64(-1) // TODO: store agent id in a file
|
agentID := readAgentID(agentIDConfigPath)
|
||||||
agentToken := c.String("grpc-token")
|
agentToken := c.String("grpc-token")
|
||||||
authClient := agentRpc.NewAuthGrpcClient(authConn, agentToken, agentID)
|
authClient := agentRpc.NewAuthGrpcClient(authConn, agentToken, agentID)
|
||||||
authInterceptor, err := agentRpc.NewAuthInterceptor(authClient, 30*time.Minute)
|
authInterceptor, err := agentRpc.NewAuthInterceptor(authClient, 30*time.Minute)
|
||||||
|
@ -178,6 +180,8 @@ func run(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeAgentID(agentID, agentIDConfigPath)
|
||||||
|
|
||||||
labels := map[string]string{
|
labels := map[string]string{
|
||||||
"hostname": hostname,
|
"hostname": hostname,
|
||||||
"platform": platform,
|
"platform": platform,
|
||||||
|
@ -280,3 +284,33 @@ func stringSliceAddToMap(sl []string, m map[string]string) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readAgentID(agentIDConfigPath string) int64 {
|
||||||
|
const defaultAgentIDValue = int64(-1)
|
||||||
|
|
||||||
|
rawAgentID, fileErr := os.ReadFile(agentIDConfigPath)
|
||||||
|
if fileErr != nil {
|
||||||
|
log.Debug().Err(fileErr).Msgf("could not open agent-id config file from %s", agentIDConfigPath)
|
||||||
|
return defaultAgentIDValue
|
||||||
|
}
|
||||||
|
|
||||||
|
strAgentID := strings.TrimSpace(string(rawAgentID))
|
||||||
|
agentID, parseErr := strconv.ParseInt(strAgentID, 10, 64)
|
||||||
|
if parseErr != nil {
|
||||||
|
log.Warn().Err(parseErr).Msg("could not parse agent-id config file content to int64")
|
||||||
|
return defaultAgentIDValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return agentID
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeAgentID(agentID int64, agentIDConfigPath string) {
|
||||||
|
currentAgentID := readAgentID(agentIDConfigPath)
|
||||||
|
|
||||||
|
if currentAgentID != agentID {
|
||||||
|
err := os.WriteFile(agentIDConfigPath, []byte(strconv.FormatInt(agentID, 10)+"\n"), 0o644)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msgf("could not write agent-id config file to %s", agentIDConfigPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -73,3 +75,91 @@ func TestStringSliceAddToMap(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadAgentIDFileNotExists(t *testing.T) {
|
||||||
|
assert.EqualValues(t, -1, readAgentID("foobar.conf"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadAgentIDFileExists(t *testing.T) {
|
||||||
|
parameters := []struct {
|
||||||
|
input string
|
||||||
|
expected int64
|
||||||
|
}{
|
||||||
|
{"42", 42},
|
||||||
|
{"42\n", 42},
|
||||||
|
{" \t42\t\r\t", 42},
|
||||||
|
{"0", 0},
|
||||||
|
{"-1", -1},
|
||||||
|
{"foo", -1},
|
||||||
|
{"1f", -1},
|
||||||
|
{"", -1},
|
||||||
|
{"-42", -42},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range parameters {
|
||||||
|
t.Run(fmt.Sprintf("Testing [%v]", i), func(t *testing.T) {
|
||||||
|
tmpF, errTmpF := os.CreateTemp("", "tmp_")
|
||||||
|
if !assert.NoError(t, errTmpF) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
errWrite := os.WriteFile(tmpF.Name(), []byte(parameters[i].input), 0o644)
|
||||||
|
if !assert.NoError(t, errWrite) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := readAgentID(tmpF.Name())
|
||||||
|
assert.EqualValues(t, parameters[i].expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteAgentIDFileNotExists(t *testing.T) {
|
||||||
|
tmpF, errTmpF := os.CreateTemp("", "tmp_")
|
||||||
|
if !assert.NoError(t, errTmpF) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
writeAgentID(42, tmpF.Name())
|
||||||
|
actual, errRead := os.ReadFile(tmpF.Name())
|
||||||
|
if !assert.NoError(t, errRead) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
assert.EqualValues(t, "42\n", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteAgentIDFileExists(t *testing.T) {
|
||||||
|
parameters := []struct {
|
||||||
|
fileInput string
|
||||||
|
writeInput int64
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"", 42, "42\n"},
|
||||||
|
{"\n", 42, "42\n"},
|
||||||
|
{"41\n", 42, "42\n"},
|
||||||
|
{"0", 42, "42\n"},
|
||||||
|
{"-1", 42, "42\n"},
|
||||||
|
{"foöbar", 42, "42\n"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range parameters {
|
||||||
|
t.Run(fmt.Sprintf("Testing [%v]", i), func(t *testing.T) {
|
||||||
|
tmpF, errTmpF := os.CreateTemp("", "tmp_")
|
||||||
|
if !assert.NoError(t, errTmpF) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
errWrite := os.WriteFile(tmpF.Name(), []byte(parameters[i].fileInput), 0o644)
|
||||||
|
if !assert.NoError(t, errWrite) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
writeAgentID(parameters[i].writeInput, tmpF.Name())
|
||||||
|
actual, errRead := os.ReadFile(tmpF.Name())
|
||||||
|
if !assert.NoError(t, errRead) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
assert.EqualValues(t, parameters[i].expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -67,6 +67,12 @@ var flags = []cli.Flag{
|
||||||
Name: "hostname",
|
Name: "hostname",
|
||||||
Usage: "agent hostname",
|
Usage: "agent hostname",
|
||||||
},
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
EnvVars: []string{"WOODPECKER_AGENT_ID_FILE"},
|
||||||
|
Name: "agent-id-config-path",
|
||||||
|
Usage: "agent-id config file path",
|
||||||
|
Value: "/etc/woodpecker/agent-id.conf",
|
||||||
|
},
|
||||||
&cli.StringSliceFlag{
|
&cli.StringSliceFlag{
|
||||||
EnvVars: []string{"WOODPECKER_FILTER_LABELS"},
|
EnvVars: []string{"WOODPECKER_FILTER_LABELS"},
|
||||||
Name: "filter",
|
Name: "filter",
|
||||||
|
|
|
@ -13,6 +13,7 @@ ENV GODEBUG=netdns=go
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
COPY --from=build /src/dist/woodpecker-agent /bin/
|
COPY --from=build /src/dist/woodpecker-agent /bin/
|
||||||
|
RUN mkdir -p /etc/woodpecker
|
||||||
|
|
||||||
HEALTHCHECK CMD ["/bin/woodpecker-agent", "ping"]
|
HEALTHCHECK CMD ["/bin/woodpecker-agent", "ping"]
|
||||||
ENTRYPOINT ["/bin/woodpecker-agent"]
|
ENTRYPOINT ["/bin/woodpecker-agent"]
|
||||||
|
|
|
@ -6,6 +6,7 @@ ARG TARGETOS TARGETARCH
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||||
--mount=type=cache,target=/go/pkg \
|
--mount=type=cache,target=/go/pkg \
|
||||||
make build-agent
|
make build-agent
|
||||||
|
RUN mkdir -p /etc/woodpecker
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
ENV GODEBUG=netdns=go
|
ENV GODEBUG=netdns=go
|
||||||
|
@ -15,6 +16,7 @@ EXPOSE 3000
|
||||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
# copy agent binary
|
# copy agent binary
|
||||||
COPY --from=build /src/dist/woodpecker-agent /bin/
|
COPY --from=build /src/dist/woodpecker-agent /bin/
|
||||||
|
COPY --from=build /etc/woodpecker /etc
|
||||||
|
|
||||||
HEALTHCHECK CMD ["/bin/woodpecker-agent", "ping"]
|
HEALTHCHECK CMD ["/bin/woodpecker-agent", "ping"]
|
||||||
ENTRYPOINT ["/bin/woodpecker-agent"]
|
ENTRYPOINT ["/bin/woodpecker-agent"]
|
||||||
|
|
|
@ -58,7 +58,7 @@ A shared secret used by server and agents to authenticate communication. A secre
|
||||||
### `WOODPECKER_AGENT_SECRET_FILE`
|
### `WOODPECKER_AGENT_SECRET_FILE`
|
||||||
> Default: empty
|
> Default: empty
|
||||||
|
|
||||||
Read the value for `WOODPECKER_AGENT_SECRET` from the specified filepath
|
Read the value for `WOODPECKER_AGENT_SECRET` from the specified filepath, e.g. `/etc/woodpecker/agent-secret.conf`
|
||||||
|
|
||||||
### `WOODPECKER_LOG_LEVEL`
|
### `WOODPECKER_LOG_LEVEL`
|
||||||
> Default: empty
|
> Default: empty
|
||||||
|
@ -80,6 +80,11 @@ Disable colored debug output.
|
||||||
|
|
||||||
Configures the agent hostname.
|
Configures the agent hostname.
|
||||||
|
|
||||||
|
### `WOODPECKER_AGENT_ID_FILE`
|
||||||
|
> Default: `/etc/woodpecker/agent-id.conf`
|
||||||
|
|
||||||
|
Configures the path of the agent-id.conf file.
|
||||||
|
|
||||||
### `WOODPECKER_MAX_WORKFLOWS`
|
### `WOODPECKER_MAX_WORKFLOWS`
|
||||||
> Default: `1`
|
> Default: `1`
|
||||||
|
|
||||||
|
|
|
@ -342,6 +342,7 @@
|
||||||
"agents": "Agents",
|
"agents": "Agents",
|
||||||
"desc": "Agents registered for this server",
|
"desc": "Agents registered for this server",
|
||||||
"none": "There are no agents yet.",
|
"none": "There are no agents yet.",
|
||||||
|
"id": "ID",
|
||||||
"add": "Add agent",
|
"add": "Add agent",
|
||||||
"save": "Save agent",
|
"save": "Save agent",
|
||||||
"show": "Show agents",
|
"show": "Show agents",
|
||||||
|
|
|
@ -66,6 +66,10 @@
|
||||||
<TextField v-model="selectedAgent.token" :placeholder="$t('admin.settings.agents.token')" disabled />
|
<TextField v-model="selectedAgent.token" :placeholder="$t('admin.settings.agents.token')" disabled />
|
||||||
</InputField>
|
</InputField>
|
||||||
|
|
||||||
|
<InputField :label="$t('admin.settings.agents.id')">
|
||||||
|
<TextField :model-value="selectedAgent.id?.toString()" disabled />
|
||||||
|
</InputField>
|
||||||
|
|
||||||
<InputField
|
<InputField
|
||||||
:label="$t('admin.settings.agents.backend.backend')"
|
:label="$t('admin.settings.agents.backend.backend')"
|
||||||
docs-url="docs/next/administration/backends/docker"
|
docs-url="docs/next/administration/backends/docker"
|
||||||
|
|
Loading…
Reference in a new issue