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:
parent
49de6e96b3
commit
31baa14e36
1
go.mod
1
go.mod
@ -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
2
go.sum
@ -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=
|
||||
|
0
hack/test/integration/dnsmasq/tftpboot/.gitkeep
Normal file
0
hack/test/integration/dnsmasq/tftpboot/.gitkeep
Normal 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
|
||||
|
@ -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'
|
||||
|
@ -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
66
pkg/download/tftp.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user