mirror of
				https://github.com/woodpecker-ci/woodpecker.git
				synced 2025-11-04 02:35:24 +00:00 
			
		
		
		
	Store agent ID in config file (#1888)
This commit is contained in:
		@@ -23,6 +23,7 @@ import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -49,6 +50,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func run(c *cli.Context) error {
 | 
			
		||||
	agentIDConfigPath := c.String("agent-id-config-path")
 | 
			
		||||
	hostname := c.String("hostname")
 | 
			
		||||
	if len(hostname) == 0 {
 | 
			
		||||
		hostname, _ = os.Hostname()
 | 
			
		||||
@@ -109,7 +111,7 @@ func run(c *cli.Context) error {
 | 
			
		||||
	}
 | 
			
		||||
	defer authConn.Close()
 | 
			
		||||
 | 
			
		||||
	agentID := int64(-1) // TODO: store agent id in a file
 | 
			
		||||
	agentID := readAgentID(agentIDConfigPath)
 | 
			
		||||
	agentToken := c.String("grpc-token")
 | 
			
		||||
	authClient := agentRpc.NewAuthGrpcClient(authConn, agentToken, agentID)
 | 
			
		||||
	authInterceptor, err := agentRpc.NewAuthInterceptor(authClient, 30*time.Minute)
 | 
			
		||||
@@ -178,6 +180,8 @@ func run(c *cli.Context) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	writeAgentID(agentID, agentIDConfigPath)
 | 
			
		||||
 | 
			
		||||
	labels := map[string]string{
 | 
			
		||||
		"hostname": hostname,
 | 
			
		||||
		"platform": platform,
 | 
			
		||||
@@ -280,3 +284,33 @@ func stringSliceAddToMap(sl []string, m map[string]string) error {
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"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",
 | 
			
		||||
		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{
 | 
			
		||||
		EnvVars: []string{"WOODPECKER_FILTER_LABELS"},
 | 
			
		||||
		Name:    "filter",
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ ENV GODEBUG=netdns=go
 | 
			
		||||
EXPOSE 3000
 | 
			
		||||
 | 
			
		||||
COPY --from=build /src/dist/woodpecker-agent /bin/
 | 
			
		||||
RUN mkdir -p /etc/woodpecker
 | 
			
		||||
 | 
			
		||||
HEALTHCHECK CMD ["/bin/woodpecker-agent", "ping"]
 | 
			
		||||
ENTRYPOINT ["/bin/woodpecker-agent"]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ ARG TARGETOS TARGETARCH
 | 
			
		||||
RUN --mount=type=cache,target=/root/.cache/go-build \
 | 
			
		||||
    --mount=type=cache,target=/go/pkg \
 | 
			
		||||
    make build-agent
 | 
			
		||||
RUN mkdir -p /etc/woodpecker
 | 
			
		||||
 | 
			
		||||
FROM scratch
 | 
			
		||||
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 agent binary
 | 
			
		||||
COPY --from=build /src/dist/woodpecker-agent /bin/
 | 
			
		||||
COPY --from=build /etc/woodpecker /etc
 | 
			
		||||
 | 
			
		||||
HEALTHCHECK CMD ["/bin/woodpecker-agent", "ping"]
 | 
			
		||||
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`
 | 
			
		||||
> 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`
 | 
			
		||||
> Default: empty
 | 
			
		||||
@@ -80,6 +80,11 @@ Disable colored debug output.
 | 
			
		||||
 | 
			
		||||
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`
 | 
			
		||||
> Default: `1`
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -342,6 +342,7 @@
 | 
			
		||||
                "agents": "Agents",
 | 
			
		||||
                "desc": "Agents registered for this server",
 | 
			
		||||
                "none": "There are no agents yet.",
 | 
			
		||||
                "id": "ID",
 | 
			
		||||
                "add": "Add agent",
 | 
			
		||||
                "save": "Save agent",
 | 
			
		||||
                "show": "Show agents",
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,10 @@
 | 
			
		||||
            <TextField v-model="selectedAgent.token" :placeholder="$t('admin.settings.agents.token')" disabled />
 | 
			
		||||
          </InputField>
 | 
			
		||||
 | 
			
		||||
          <InputField :label="$t('admin.settings.agents.id')">
 | 
			
		||||
            <TextField :model-value="selectedAgent.id?.toString()" disabled />
 | 
			
		||||
          </InputField>
 | 
			
		||||
 | 
			
		||||
          <InputField
 | 
			
		||||
            :label="$t('admin.settings.agents.backend.backend')"
 | 
			
		||||
            docs-url="docs/next/administration/backends/docker"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user