mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	tsic: Tailscale in Container abstraction
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
		
							parent
							
								
									a39504510a
								
							
						
					
					
						commit
						fa8b02a83f
					
				
							
								
								
									
										217
									
								
								integration/tsic/tsic.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								integration/tsic/tsic.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,217 @@ | ||||
| package tsic | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net/netip" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/juanfont/headscale" | ||||
| 	"github.com/juanfont/headscale/integration/dockertestutil" | ||||
| 	"github.com/ory/dockertest/v3" | ||||
| 	"github.com/ory/dockertest/v3/docker" | ||||
| ) | ||||
| 
 | ||||
| const tsicHashLength = 6 | ||||
| const dockerContextPath = "../." | ||||
| 
 | ||||
| var errTailscalePingFailed = errors.New("ping failed") | ||||
| 
 | ||||
| type TailscaleInContainer struct { | ||||
| 	version  string | ||||
| 	Hostname string | ||||
| 
 | ||||
| 	pool      *dockertest.Pool | ||||
| 	container *dockertest.Resource | ||||
| 	network   *dockertest.Network | ||||
| } | ||||
| 
 | ||||
| func New( | ||||
| 	pool *dockertest.Pool, | ||||
| 	version string, | ||||
| 	network *dockertest.Network) (*TailscaleInContainer, error) { | ||||
| 	hash, err := headscale.GenerateRandomStringDNSSafe(tsicHashLength) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	hostname := fmt.Sprintf("ts-%s-%s", version, hash) | ||||
| 
 | ||||
| 	// TODO(kradalby): figure out why we need to "refresh" the network here.
 | ||||
| 	// network, err = dockertestutil.GetFirstOrCreateNetwork(pool, network.Network.Name)
 | ||||
| 	// if err != nil {
 | ||||
| 	// 	return nil, err
 | ||||
| 	// }
 | ||||
| 
 | ||||
| 	tailscaleOptions := &dockertest.RunOptions{ | ||||
| 		Name:     hostname, | ||||
| 		Networks: []*dockertest.Network{network}, | ||||
| 		Cmd: []string{ | ||||
| 			"tailscaled", "--tun=tsdev", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	// dockertest isnt very good at handling containers that has already
 | ||||
| 	// been created, this is an attempt to make sure this container isnt
 | ||||
| 	// present.
 | ||||
| 	err = pool.RemoveContainerByName(hostname) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	container, err := pool.BuildAndRunWithBuildOptions( | ||||
| 		createTailscaleBuildOptions(version), | ||||
| 		tailscaleOptions, | ||||
| 		dockertestutil.DockerRestartPolicy, | ||||
| 		dockertestutil.DockerAllowLocalIPv6, | ||||
| 		dockertestutil.DockerAllowNetworkAdministration, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("could not start tailscale container: %w", err) | ||||
| 	} | ||||
| 	log.Printf("Created %s container\n", hostname) | ||||
| 
 | ||||
| 	return &TailscaleInContainer{ | ||||
| 		version:  version, | ||||
| 		Hostname: hostname, | ||||
| 
 | ||||
| 		pool:      pool, | ||||
| 		container: container, | ||||
| 		network:   network, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (t *TailscaleInContainer) Shutdown() error { | ||||
| 	return t.pool.Purge(t.container) | ||||
| } | ||||
| 
 | ||||
| func (t *TailscaleInContainer) Up( | ||||
| 	loginServer, authKey string, | ||||
| ) error { | ||||
| 	command := []string{ | ||||
| 		"tailscale", | ||||
| 		"up", | ||||
| 		"-login-server", | ||||
| 		loginServer, | ||||
| 		"--authkey", | ||||
| 		authKey, | ||||
| 		"--hostname", | ||||
| 		t.Hostname, | ||||
| 	} | ||||
| 
 | ||||
| 	log.Println("Join command:", command) | ||||
| 	log.Printf("Running join command for %s\n", t.Hostname) | ||||
| 	_, _, err := dockertestutil.ExecuteCommand( | ||||
| 		t.container, | ||||
| 		command, | ||||
| 		[]string{}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	log.Printf("%s joined\n", t.Hostname) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (t *TailscaleInContainer) IPs() ([]netip.Addr, error) { | ||||
| 	ips := make([]netip.Addr, 0) | ||||
| 
 | ||||
| 	command := []string{ | ||||
| 		"tailscale", | ||||
| 		"ip", | ||||
| 	} | ||||
| 
 | ||||
| 	result, _, err := dockertestutil.ExecuteCommand( | ||||
| 		t.container, | ||||
| 		command, | ||||
| 		[]string{}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return []netip.Addr{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, address := range strings.Split(result, "\n") { | ||||
| 		address = strings.TrimSuffix(address, "\n") | ||||
| 		if len(address) < 1 { | ||||
| 			continue | ||||
| 		} | ||||
| 		ip, err := netip.ParseAddr(address) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		ips = append(ips, ip) | ||||
| 	} | ||||
| 
 | ||||
| 	return ips, nil | ||||
| } | ||||
| 
 | ||||
| func (t *TailscaleInContainer) Ping(ip netip.Addr) error { | ||||
| 	command := []string{ | ||||
| 		"tailscale", "ping", | ||||
| 		"--timeout=1s", | ||||
| 		"--c=10", | ||||
| 		"--until-direct=true", | ||||
| 		ip.String(), | ||||
| 	} | ||||
| 
 | ||||
| 	result, _, err := dockertestutil.ExecuteCommand( | ||||
| 		t.container, | ||||
| 		command, | ||||
| 		[]string{}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if !strings.Contains(result, "pong") { | ||||
| 		return errTailscalePingFailed | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func createTailscaleBuildOptions(version string) *dockertest.BuildOptions { | ||||
| 	var tailscaleBuildOptions *dockertest.BuildOptions | ||||
| 	switch version { | ||||
| 	case "head": | ||||
| 		tailscaleBuildOptions = &dockertest.BuildOptions{ | ||||
| 			Dockerfile: "Dockerfile.tailscale-HEAD", | ||||
| 			ContextDir: dockerContextPath, | ||||
| 			BuildArgs:  []docker.BuildArg{}, | ||||
| 		} | ||||
| 	case "unstable": | ||||
| 		tailscaleBuildOptions = &dockertest.BuildOptions{ | ||||
| 			Dockerfile: "Dockerfile.tailscale", | ||||
| 			ContextDir: dockerContextPath, | ||||
| 			BuildArgs: []docker.BuildArg{ | ||||
| 				{ | ||||
| 					Name:  "TAILSCALE_VERSION", | ||||
| 					Value: "*", // Installs the latest version https://askubuntu.com/a/824926
 | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name:  "TAILSCALE_CHANNEL", | ||||
| 					Value: "unstable", | ||||
| 				}, | ||||
| 			}, | ||||
| 		} | ||||
| 	default: | ||||
| 		tailscaleBuildOptions = &dockertest.BuildOptions{ | ||||
| 			Dockerfile: "Dockerfile.tailscale", | ||||
| 			ContextDir: dockerContextPath, | ||||
| 			BuildArgs: []docker.BuildArg{ | ||||
| 				{ | ||||
| 					Name:  "TAILSCALE_VERSION", | ||||
| 					Value: version, | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name:  "TAILSCALE_CHANNEL", | ||||
| 					Value: "stable", | ||||
| 				}, | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return tailscaleBuildOptions | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user