feat: add support for tftp download

This adds support for downloading the machine config over TFTP. This
will allow users to avoid having to setup an HTTP server, and use
whatever they are using for PXE.

Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
This commit is contained in:
Andrew Rynhard 2019-12-17 19:53:19 +00:00
parent 49de6e96b3
commit 31baa14e36
7 changed files with 90 additions and 15 deletions

1
go.mod
View File

@ -50,6 +50,7 @@ require (
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v1.0.0-rc8 // indirect
github.com/opencontainers/runtime-spec v1.0.1
github.com/pin/tftp v2.1.0+incompatible
github.com/prometheus/procfs v0.0.6
github.com/ryanuber/columnize v2.1.0+incompatible
github.com/spf13/cobra v0.0.5

2
go.sum
View File

@ -362,6 +362,8 @@ github.com/pborman/uuid v0.0.0-20150603214016-ca53cad383ca h1:dKRMHfduZ/ZqOHuYGk
github.com/pborman/uuid v0.0.0-20150603214016-ca53cad383ca/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pin/tftp v2.1.0+incompatible h1:Yng4J7jv6lOc6IF4XoB5mnd3P7ZrF60XQq+my3FAMus=
github.com/pin/tftp v2.1.0+incompatible/go.mod h1:xVpZOMCXTy+A5QMjEVN0Glwa1sUvaJhFXbr/aAxuxGY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View File

@ -10,6 +10,7 @@ services:
ipv4_address: 172.28.1.1
volumes:
- ./dnsmasq/talos0.conf:/etc/dnsmasq.conf:Z
- ./dnsmasq/tftpboot:/var/lib/tftpboot
cap_add:
- NET_ADMIN
restart: always

View File

@ -53,7 +53,7 @@ function up {
echo ${INSTALLER}
cp $PWD/../../../build/initramfs.xz ./matchbox/assets/
cp $PWD/../../../build/vmlinuz ./matchbox/assets/
cd matchbox/assets
cd ./matchbox/assets
$PWD/../../../../../build/osctl-linux-amd64 config generate --install-image ${INSTALLER} integration-test https://kubernetes.talos.dev:6443
yq w -i init.yaml machine.install.extraKernelArgs[+] 'console=ttyS0'
yq w -i init.yaml cluster.network.cni.name 'custom'

View File

@ -67,8 +67,9 @@ func Download(endpoint string, opts ...Option) (b []byte, err error) {
opt(dlOpts)
}
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
var req *http.Request
if req, err = http.NewRequest(http.MethodGet, u.String(), nil); err != nil {
return b, err
}
@ -82,27 +83,26 @@ func Download(endpoint string, opts ...Option) (b []byte, err error) {
return retry.ExpectedError(err)
}
if dlOpts.Format == b64 {
var b64 []byte
b64, err = base64.StdEncoding.DecodeString(string(b))
if err != nil {
return err
}
b = b64
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to download config from %q: %w", u.String(), err)
}
if dlOpts.Format == b64 {
var b64 []byte
b64, err = base64.StdEncoding.DecodeString(string(b))
if err != nil {
return nil, err
}
b = b64
}
return b, nil
}
// download handles the actual http request
func download(req *http.Request) (data []byte, err error) {
client := &http.Client{}
@ -124,3 +124,8 @@ func download(req *http.Request) (data []byte, err error) {
return data, err
}
func init() {
transport := (http.DefaultTransport.(*http.Transport))
transport.RegisterProtocol("tftp", NewTFTPTransport())
}

66
pkg/download/tftp.go Normal file
View File

@ -0,0 +1,66 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package download
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"github.com/pin/tftp"
)
// NewTFTPTransport returns an http.RoundTripper capable of handling the TFTP
// protocol.
func NewTFTPTransport() http.RoundTripper {
return &tftpRoundTripper{}
}
var _ http.RoundTripper = &tftpRoundTripper{}
type tftpRoundTripper struct{}
func (t *tftpRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
addr := req.URL.Host
if req.URL.Port() == "" {
addr += ":69"
}
c, err := tftp.NewClient(addr)
if err != nil {
return nil, err
}
w, err := c.Receive(req.URL.Path, "octet")
if err != nil {
return nil, err
}
buf := &bytes.Buffer{}
written, err := w.WriteTo(buf)
if err != nil {
return nil, err
}
if expected, ok := w.(tftp.IncomingTransfer).Size(); ok {
if written != expected {
return nil, fmt.Errorf("expected %d bytes, got %d", expected, written)
}
}
return &http.Response{
Status: "200 OK",
StatusCode: 200,
Proto: "TFTP/1.0",
ProtoMajor: 1,
ProtoMinor: 0,
Body: ioutil.NopCloser(buf),
ContentLength: -1,
Request: req,
}, nil
}