mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-01-08 16:45:30 +00:00
Fix IPv6 host aliases for kubernetes (#2992)
Closes #2991 [Tests](https://github.com/woodpecker-ci/woodpecker/pull/2993#issuecomment-1868048169) --------- Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
parent
cd9d425a0d
commit
253d702bc7
10 changed files with 131 additions and 69 deletions
|
@ -88,8 +88,12 @@ func toHostConfig(step *types.Step) *container.HostConfig {
|
|||
if len(step.DNSSearch) != 0 {
|
||||
config.DNSSearch = step.DNSSearch
|
||||
}
|
||||
extraHosts := []string{}
|
||||
for _, hostAlias := range step.ExtraHosts {
|
||||
extraHosts = append(extraHosts, hostAlias.Name+":"+hostAlias.IP)
|
||||
}
|
||||
if len(step.ExtraHosts) != 0 {
|
||||
config.ExtraHosts = step.ExtraHosts
|
||||
config.ExtraHosts = extraHosts
|
||||
}
|
||||
if len(step.Devices) != 0 {
|
||||
config.Devices = toDev(step.Devices)
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
@ -166,8 +165,7 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s
|
|||
}
|
||||
}
|
||||
|
||||
extraHosts := []string{}
|
||||
|
||||
extraHosts := []types.HostAlias{}
|
||||
for _, stage := range conf.Stages {
|
||||
if stage.Alias == "services" {
|
||||
for _, step := range stage.Steps {
|
||||
|
@ -175,12 +173,12 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
extraHosts = append(extraHosts, step.Networks[0].Aliases[0]+":"+svc.Spec.ClusterIP)
|
||||
hostAlias := types.HostAlias{Name: step.Networks[0].Aliases[0], IP: svc.Spec.ClusterIP}
|
||||
extraHosts = append(extraHosts, hostAlias)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Trace().Msgf("Adding extra hosts: %s", strings.Join(extraHosts, ", "))
|
||||
log.Trace().Msgf("Adding extra hosts: %v", extraHosts)
|
||||
for _, stage := range conf.Stages {
|
||||
for _, step := range stage.Steps {
|
||||
step.ExtraHosts = extraHosts
|
||||
|
|
|
@ -36,17 +36,16 @@ const (
|
|||
|
||||
func mkPod(namespace, name, image, workDir, goos, serviceAccountName string,
|
||||
pool, privileged bool,
|
||||
commands, vols, extraHosts []string,
|
||||
commands, vols []string,
|
||||
labels, annotations, env, nodeSelector map[string]string,
|
||||
tolerations []types.Toleration, resources types.Resources,
|
||||
extraHosts []types.HostAlias, tolerations []types.Toleration, resources types.Resources,
|
||||
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
|
||||
) (*v1.Pod, error) {
|
||||
var err error
|
||||
|
||||
meta := podMeta(name, namespace, labels, annotations)
|
||||
|
||||
spec, err := podSpec(serviceAccountName, vols, extraHosts, env,
|
||||
nodeSelector, tolerations, securityContext, securityContextConfig)
|
||||
spec, err := podSpec(serviceAccountName, vols, env, nodeSelector, extraHosts, tolerations, securityContext, securityContextConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -86,7 +85,8 @@ func podMeta(name, namespace string, labels, annotations map[string]string) meta
|
|||
return meta
|
||||
}
|
||||
|
||||
func podSpec(serviceAccountName string, vols, extraHosts []string, env, backendNodeSelector map[string]string, backendTolerations []types.Toleration,
|
||||
func podSpec(serviceAccountName string, vols []string, env, backendNodeSelector map[string]string,
|
||||
extraHosts []types.HostAlias, backendTolerations []types.Toleration,
|
||||
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
|
||||
) (v1.PodSpec, error) {
|
||||
var err error
|
||||
|
@ -195,7 +195,7 @@ func volumeMount(name, path string) v1.VolumeMount {
|
|||
}
|
||||
|
||||
// Here is the service IPs (placed in /etc/hosts in the Pod)
|
||||
func hostAliases(extraHosts []string) []v1.HostAlias {
|
||||
func hostAliases(extraHosts []types.HostAlias) []v1.HostAlias {
|
||||
hostAliases := []v1.HostAlias{}
|
||||
for _, extraHost := range extraHosts {
|
||||
hostAlias := hostAlias(extraHost)
|
||||
|
@ -204,11 +204,10 @@ func hostAliases(extraHosts []string) []v1.HostAlias {
|
|||
return hostAliases
|
||||
}
|
||||
|
||||
func hostAlias(extraHost string) v1.HostAlias {
|
||||
host := strings.Split(extraHost, ":")
|
||||
func hostAlias(extraHost types.HostAlias) v1.HostAlias {
|
||||
return v1.HostAlias{
|
||||
IP: host[1],
|
||||
Hostnames: []string{host[0]},
|
||||
IP: extraHost.IP,
|
||||
Hostnames: []string{extraHost.Name},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -358,9 +357,9 @@ func startPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, err
|
|||
|
||||
pod, err := mkPod(engine.config.Namespace, podName, step.Image, step.WorkingDir, engine.goos, step.BackendOptions.Kubernetes.ServiceAccountName,
|
||||
step.Pull, step.Privileged,
|
||||
step.Commands, step.Volumes, step.ExtraHosts,
|
||||
step.Commands, step.Volumes,
|
||||
engine.config.PodLabels, engine.config.PodAnnotations, step.Environment, step.BackendOptions.Kubernetes.NodeSelector,
|
||||
step.BackendOptions.Kubernetes.Tolerations, step.BackendOptions.Kubernetes.Resources, step.BackendOptions.Kubernetes.SecurityContext, engine.config.SecurityContext)
|
||||
step.ExtraHosts, step.BackendOptions.Kubernetes.Tolerations, step.BackendOptions.Kubernetes.Resources, step.BackendOptions.Kubernetes.SecurityContext, engine.config.SecurityContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -108,9 +108,9 @@ func TestTinyPod(t *testing.T) {
|
|||
|
||||
pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "gradle:8.4.0-jdk21", "/woodpecker/src", "linux/amd64", "",
|
||||
false, false,
|
||||
[]string{"gradle build"}, []string{"workspace:/woodpecker/src"}, nil,
|
||||
[]string{"gradle build"}, []string{"workspace:/woodpecker/src"},
|
||||
nil, nil, map[string]string{"CI": "woodpecker"}, nil,
|
||||
nil,
|
||||
nil, nil,
|
||||
types.Resources{Requests: nil, Limits: nil}, nil, SecurityContextConfig{},
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
@ -228,17 +228,27 @@ func TestFullPod(t *testing.T) {
|
|||
"hostnames": [
|
||||
"cloudflare"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ip": "2606:4700:4700::64",
|
||||
"hostnames": [
|
||||
"cf.v6"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {}
|
||||
}`
|
||||
|
||||
hostAliases := []types.HostAlias{
|
||||
{Name: "cloudflare", IP: "1.1.1.1"},
|
||||
{Name: "cf.v6", IP: "2606:4700:4700::64"},
|
||||
}
|
||||
pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "meltwater/drone-cache", "/woodpecker/src", "linux/amd64", "wp-svc-acc",
|
||||
true, true,
|
||||
[]string{"go get", "go test"}, []string{"woodpecker-cache:/woodpecker/src/cache"}, []string{"cloudflare:1.1.1.1"},
|
||||
[]string{"go get", "go test"}, []string{"woodpecker-cache:/woodpecker/src/cache"},
|
||||
map[string]string{"app": "test"}, map[string]string{"apparmor.security": "runtime/default"}, map[string]string{"CGO": "0"}, map[string]string{"storage": "ssd"},
|
||||
[]types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}},
|
||||
hostAliases, []types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}},
|
||||
types.Resources{Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"}, Limits: map[string]string{"memory": "256Mi", "cpu": "2"}},
|
||||
&types.SecurityContext{Privileged: newBool(true), RunAsNonRoot: newBool(true), RunAsUser: newInt64(101), RunAsGroup: newInt64(101), FSGroup: newInt64(101)},
|
||||
SecurityContextConfig{RunAsNonRoot: false},
|
||||
|
|
|
@ -18,3 +18,8 @@ package types
|
|||
type Network struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type HostAlias struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ type Step struct {
|
|||
Environment map[string]string `json:"environment,omitempty"`
|
||||
Entrypoint []string `json:"entrypoint,omitempty"`
|
||||
Commands []string `json:"commands,omitempty"`
|
||||
ExtraHosts []string `json:"extra_hosts,omitempty"`
|
||||
ExtraHosts []HostAlias `json:"extra_hosts,omitempty"`
|
||||
Volumes []string `json:"volumes,omitempty"`
|
||||
Tmpfs []string `json:"tmpfs,omitempty"`
|
||||
Devices []string `json:"devices,omitempty"`
|
||||
|
|
|
@ -82,14 +82,15 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Name: "test_clone",
|
||||
Alias: "clone",
|
||||
Steps: []*backend_types.Step{{
|
||||
Name: "test_clone",
|
||||
Alias: "clone",
|
||||
Type: backend_types.StepTypeClone,
|
||||
Image: constant.DefaultCloneImage,
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"clone"}}},
|
||||
Name: "test_clone",
|
||||
Alias: "clone",
|
||||
Type: backend_types.StepTypeClone,
|
||||
Image: constant.DefaultCloneImage,
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"clone"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
}
|
||||
|
||||
|
@ -126,14 +127,15 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Name: "test_stage_0",
|
||||
Alias: "dummy",
|
||||
Steps: []*backend_types.Step{{
|
||||
Name: "test_step_0",
|
||||
Alias: "dummy",
|
||||
Type: backend_types.StepTypePlugin,
|
||||
Image: "dummy_img",
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"dummy"}}},
|
||||
Name: "test_step_0",
|
||||
Alias: "dummy",
|
||||
Type: backend_types.StepTypePlugin,
|
||||
Image: "dummy_img",
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"dummy"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
|
@ -161,39 +163,42 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Name: "test_stage_0",
|
||||
Alias: "echo env",
|
||||
Steps: []*backend_types.Step{{
|
||||
Name: "test_step_0",
|
||||
Alias: "echo env",
|
||||
Type: backend_types.StepTypeCommands,
|
||||
Image: "bash",
|
||||
Commands: []string{"env"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo env"}}},
|
||||
Name: "test_step_0",
|
||||
Alias: "echo env",
|
||||
Type: backend_types.StepTypeCommands,
|
||||
Image: "bash",
|
||||
Commands: []string{"env"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo env"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
}, {
|
||||
Name: "test_stage_1",
|
||||
Alias: "parallel echo 1",
|
||||
Steps: []*backend_types.Step{{
|
||||
Name: "test_step_1",
|
||||
Alias: "parallel echo 1",
|
||||
Type: backend_types.StepTypeCommands,
|
||||
Image: "bash",
|
||||
Commands: []string{"echo 1"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 1"}}},
|
||||
Name: "test_step_1",
|
||||
Alias: "parallel echo 1",
|
||||
Type: backend_types.StepTypeCommands,
|
||||
Image: "bash",
|
||||
Commands: []string{"echo 1"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 1"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}, {
|
||||
Name: "test_step_2",
|
||||
Alias: "parallel echo 2",
|
||||
Type: backend_types.StepTypeCommands,
|
||||
Image: "bash",
|
||||
Commands: []string{"echo 2"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 2"}}},
|
||||
Name: "test_step_2",
|
||||
Alias: "parallel echo 2",
|
||||
Type: backend_types.StepTypeCommands,
|
||||
Image: "bash",
|
||||
Commands: []string{"echo 2"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 2"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
|
|
|
@ -55,6 +55,16 @@ func (c *Compiler) createProcess(name string, container *yaml_types.Container, s
|
|||
})
|
||||
}
|
||||
|
||||
extraHosts := make([]backend_types.HostAlias, len(container.ExtraHosts))
|
||||
for i, extraHost := range container.ExtraHosts {
|
||||
name, ip, ok := strings.Cut(extraHost, ":")
|
||||
if !ok {
|
||||
return nil, &ErrExtraHostFormat{host: extraHost}
|
||||
}
|
||||
extraHosts[i].Name = name
|
||||
extraHosts[i].IP = ip
|
||||
}
|
||||
|
||||
var volumes []string
|
||||
if !c.local {
|
||||
volumes = append(volumes, workspace)
|
||||
|
@ -173,7 +183,7 @@ func (c *Compiler) createProcess(name string, container *yaml_types.Container, s
|
|||
WorkingDir: workingdir,
|
||||
Environment: environment,
|
||||
Commands: container.Commands,
|
||||
ExtraHosts: container.ExtraHosts,
|
||||
ExtraHosts: extraHosts,
|
||||
Volumes: volumes,
|
||||
Tmpfs: container.Tmpfs,
|
||||
Devices: container.Devices,
|
||||
|
|
30
pipeline/frontend/yaml/compiler/errors.go
Normal file
30
pipeline/frontend/yaml/compiler/errors.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
// 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 compiler
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ErrExtraHostFormat struct {
|
||||
host string
|
||||
}
|
||||
|
||||
func (err *ErrExtraHostFormat) Error() string {
|
||||
return fmt.Sprintf("extra host %s is in wrong format", err.host)
|
||||
}
|
||||
|
||||
func (*ErrExtraHostFormat) Is(target error) bool {
|
||||
_, ok := target.(*ErrExtraHostFormat) //nolint:errorlint
|
||||
return ok
|
||||
}
|
|
@ -45,6 +45,7 @@ environment:
|
|||
extra_hosts:
|
||||
- somehost:162.242.195.82
|
||||
- otherhost:50.31.209.229
|
||||
- ipv6:2001:db8::10
|
||||
name: my-build-container
|
||||
network_mode: bridge
|
||||
networks:
|
||||
|
@ -84,7 +85,7 @@ func TestUnmarshalContainer(t *testing.T) {
|
|||
DNS: base.StringOrSlice{"8.8.8.8"},
|
||||
DNSSearch: base.StringOrSlice{"example.com"},
|
||||
Environment: base.SliceOrMap{"RACK_ENV": "development", "SHOW": "true"},
|
||||
ExtraHosts: []string{"somehost:162.242.195.82", "otherhost:50.31.209.229"},
|
||||
ExtraHosts: []string{"somehost:162.242.195.82", "otherhost:50.31.209.229", "ipv6:2001:db8::10"},
|
||||
Image: "golang:latest",
|
||||
MemLimit: base.MemStringOrInt(1024),
|
||||
MemSwapLimit: base.MemStringOrInt(1024),
|
||||
|
|
Loading…
Reference in a new issue