mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-26 10:20:29 +00:00
Add agent config file (#1971)
This commit is contained in:
parent
a4c791c6e5
commit
fa951a8e95
7 changed files with 191 additions and 139 deletions
|
@ -23,7 +23,6 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -50,7 +49,7 @@ import (
|
|||
)
|
||||
|
||||
func run(c *cli.Context) error {
|
||||
agentIDConfigPath := c.String("agent-id-config-path")
|
||||
agentConfigPath := c.String("agent-config")
|
||||
hostname := c.String("hostname")
|
||||
if len(hostname) == 0 {
|
||||
hostname, _ = os.Hostname()
|
||||
|
@ -111,9 +110,15 @@ func run(c *cli.Context) error {
|
|||
}
|
||||
defer authConn.Close()
|
||||
|
||||
agentID := readAgentID(agentIDConfigPath)
|
||||
agentConfig := readAgentConfig(agentConfigPath)
|
||||
|
||||
// deprecated
|
||||
if agentConfig.AgentID == defaultAgentIDValue {
|
||||
agentConfig.AgentID = readAgentID(c.String("agent-id-config-path"))
|
||||
}
|
||||
|
||||
agentToken := c.String("grpc-token")
|
||||
authClient := agentRpc.NewAuthGrpcClient(authConn, agentToken, agentID)
|
||||
authClient := agentRpc.NewAuthGrpcClient(authConn, agentToken, agentConfig.AgentID)
|
||||
authInterceptor, err := agentRpc.NewAuthInterceptor(authClient, 30*time.Minute)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -175,12 +180,12 @@ func run(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
agentID, err = client.RegisterAgent(ctx, platform, engine.Name(), version.String(), parallel)
|
||||
agentConfig.AgentID, err = client.RegisterAgent(ctx, platform, engine.Name(), version.String(), parallel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeAgentID(agentID, agentIDConfigPath)
|
||||
writeAgentConfig(agentConfig, agentConfigPath)
|
||||
|
||||
labels := map[string]string{
|
||||
"hostname": hostname,
|
||||
|
@ -197,7 +202,7 @@ func run(c *cli.Context) error {
|
|||
Labels: labels,
|
||||
}
|
||||
|
||||
log.Debug().Msgf("Agent registered with ID %d", agentID)
|
||||
log.Debug().Msgf("Agent registered with ID %d", agentConfig.AgentID)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
|
@ -284,33 +289,3 @@ 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,8 +15,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -75,91 +73,3 @@ 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
94
cmd/agent/config.go
Normal file
94
cmd/agent/config.go
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2022 Woodpecker Authors
|
||||
// Copyright 2019 Laszlo Fogas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type AgentConfig struct {
|
||||
AgentID int64 `json:"agent_id"`
|
||||
}
|
||||
|
||||
const defaultAgentIDValue = int64(-1)
|
||||
|
||||
func readAgentConfig(agentConfigPath string) AgentConfig {
|
||||
conf := AgentConfig{
|
||||
AgentID: defaultAgentIDValue,
|
||||
}
|
||||
|
||||
rawAgentConf, err := os.ReadFile(agentConfigPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
log.Info().Msgf("no agent config found at '%s', start with defaults", agentConfigPath)
|
||||
} else {
|
||||
log.Error().Err(err).Msgf("could not open agent config at '%s'", agentConfigPath)
|
||||
}
|
||||
return conf
|
||||
}
|
||||
if strings.TrimSpace(string(rawAgentConf)) == "" {
|
||||
return conf
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(rawAgentConf, &conf); err != nil {
|
||||
log.Error().Err(err).Msg("could not parse agent config")
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
func writeAgentConfig(conf AgentConfig, agentConfigPath string) {
|
||||
rawAgentConf, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not marshal agent config")
|
||||
return
|
||||
}
|
||||
|
||||
// get old config
|
||||
oldRawAgentConf, _ := os.ReadFile(agentConfigPath)
|
||||
|
||||
// if config differ write to disk
|
||||
if bytes.Equal(rawAgentConf, oldRawAgentConf) {
|
||||
if err := os.WriteFile(agentConfigPath, rawAgentConf, 0o644); err != nil {
|
||||
log.Error().Err(err).Msgf("could not persist agent config at '%s'", agentConfigPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deprecated
|
||||
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
|
||||
}
|
61
cmd/agent/config_test.go
Normal file
61
cmd/agent/config_test.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -68,10 +68,10 @@ var flags = []cli.Flag{
|
|||
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",
|
||||
EnvVars: []string{"WOODPECKER_AGENT_CONFIG_FILE"},
|
||||
Name: "agent-config",
|
||||
Usage: "agent config file path",
|
||||
Value: "/etc/woodpecker/agent.conf",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
EnvVars: []string{"WOODPECKER_FILTER_LABELS"},
|
||||
|
@ -208,4 +208,13 @@ var flags = []cli.Flag{
|
|||
Usage: "duration to wait before retrying to connect to the server",
|
||||
Value: time.Second * 2,
|
||||
},
|
||||
|
||||
// DEPRECATED
|
||||
&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",
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ services:
|
|||
depends_on:
|
||||
- woodpecker-server
|
||||
volumes:
|
||||
- woodpecker-agent-config:/etc/woodpecker
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- WOODPECKER_SERVER=woodpecker-server:9000
|
||||
|
|
|
@ -8,10 +8,12 @@ version: '3'
|
|||
|
||||
services:
|
||||
woodpecker-agent:
|
||||
[...]
|
||||
environment:
|
||||
+ - WOODPECKER_SERVER=localhost:9000
|
||||
+ - WOODPECKER_AGENT_SECRET="your-shared-secret-goes-here"
|
||||
[...]
|
||||
volumes:
|
||||
- woodpecker-agent-config:/etc/woodpecker
|
||||
environment:
|
||||
+ - WOODPECKER_SERVER=localhost:9000
|
||||
+ - WOODPECKER_AGENT_SECRET="your-shared-secret-goes-here"
|
||||
```
|
||||
|
||||
The following are automatically set and can be overridden:
|
||||
|
@ -49,7 +51,7 @@ In that case registration process would be as follows:
|
|||
|
||||
1. First time Agent communicates with Server using system token;
|
||||
2. Server registers Agent in DB, generates ID and sends this ID back to Agent;
|
||||
3. Agent stores ID in a file configured by `WOODPECKER_AGENT_ID_FILE`.
|
||||
3. Agent stores ID in a file configured by `WOODPECKER_AGENT_CONFIG_FILE`.
|
||||
|
||||
At the following startups Agent uses system token **and** ID.
|
||||
|
||||
|
@ -113,10 +115,10 @@ Disable colored debug output.
|
|||
|
||||
Configures the agent hostname.
|
||||
|
||||
### `WOODPECKER_AGENT_ID_FILE`
|
||||
> Default: `/etc/woodpecker/agent-id.conf`
|
||||
### `WOODPECKER_AGENT_CONFIG_FILE`
|
||||
> Default: `/etc/woodpecker/agent.conf`
|
||||
|
||||
Configures the path of the agent-id.conf file.
|
||||
Configures the path of the agent config file.
|
||||
|
||||
### `WOODPECKER_MAX_WORKFLOWS`
|
||||
> Default: `1`
|
||||
|
|
Loading…
Reference in a new issue