mirror of
https://github.com/rancher/os.git
synced 2025-09-02 15:24:32 +00:00
Added tftp datasource for cloud config.
This commit is contained in:
committed by
niusmallnan
parent
8b75752225
commit
66c5f6130a
83
config/cloudinit/datasource/tftp/tftp.go
Normal file
83
config/cloudinit/datasource/tftp/tftp.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package tftp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/os/config/cloudinit/datasource"
|
||||
|
||||
"github.com/pin/tftp"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
Receive(filename string, mode string) (io.WriterTo, error)
|
||||
}
|
||||
|
||||
type RemoteFile struct {
|
||||
host string
|
||||
path string
|
||||
client Client
|
||||
stream io.WriterTo
|
||||
lastError error
|
||||
}
|
||||
|
||||
func NewDatasource(hostAndPath string) *RemoteFile {
|
||||
parts := strings.SplitN(hostAndPath, "/", 2)
|
||||
|
||||
if len(parts) < 2 {
|
||||
return &RemoteFile{hostAndPath, "", nil, nil, nil}
|
||||
}
|
||||
|
||||
host := parts[0]
|
||||
if match, _ := regexp.MatchString(":[0-9]{2,5}$", host); !match {
|
||||
// No port, using default port 69
|
||||
host += ":69"
|
||||
}
|
||||
|
||||
path := parts[1]
|
||||
if client, lastError := tftp.NewClient(host); lastError == nil {
|
||||
return &RemoteFile{host, path, client, nil, nil}
|
||||
}
|
||||
|
||||
return &RemoteFile{host, path, nil, nil, nil}
|
||||
}
|
||||
|
||||
func (f *RemoteFile) IsAvailable() bool {
|
||||
f.stream, f.lastError = f.client.Receive(f.path, "octet")
|
||||
return f.lastError == nil
|
||||
}
|
||||
|
||||
func (f *RemoteFile) Finish() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RemoteFile) String() string {
|
||||
return fmt.Sprintf("%s: %s%s (lastError: %v)", f.Type(), f.host, f.path, f.lastError)
|
||||
}
|
||||
|
||||
func (f *RemoteFile) AvailabilityChanges() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *RemoteFile) ConfigRoot() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *RemoteFile) FetchMetadata() (datasource.Metadata, error) {
|
||||
return datasource.Metadata{}, nil
|
||||
}
|
||||
|
||||
func (f *RemoteFile) FetchUserdata() ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
_, err := f.stream.WriteTo(&b)
|
||||
|
||||
return b.Bytes(), err
|
||||
}
|
||||
|
||||
func (f *RemoteFile) Type() string {
|
||||
return "tftp"
|
||||
}
|
92
config/cloudinit/datasource/tftp/tftp_test.go
Normal file
92
config/cloudinit/datasource/tftp/tftp_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package tftp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type mockClient struct {
|
||||
}
|
||||
|
||||
type mockReceiver struct {
|
||||
}
|
||||
|
||||
func (r mockReceiver) WriteTo(w io.Writer) (n int64, err error) {
|
||||
b := []byte("cloud-config file")
|
||||
w.Write(b)
|
||||
return int64(len(b)), nil
|
||||
}
|
||||
|
||||
func (c mockClient) Receive(filename string, mode string) (io.WriterTo, error) {
|
||||
if filename == "does-not-exist" {
|
||||
return &mockReceiver{}, fmt.Errorf("does not exist")
|
||||
}
|
||||
return &mockReceiver{}, nil
|
||||
}
|
||||
|
||||
var _ Client = (*mockClient)(nil)
|
||||
|
||||
func TestNewDatasource(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
root string
|
||||
expectHost string
|
||||
expectPath string
|
||||
}{
|
||||
{
|
||||
root: "127.0.0.1/test/file.yaml",
|
||||
expectHost: "127.0.0.1:69",
|
||||
expectPath: "test/file.yaml",
|
||||
},
|
||||
{
|
||||
root: "127.0.0.1/test/file.yaml",
|
||||
expectHost: "127.0.0.1:69",
|
||||
expectPath: "test/file.yaml",
|
||||
},
|
||||
} {
|
||||
ds := NewDatasource(tt.root)
|
||||
if ds.host != tt.expectHost || ds.path != tt.expectPath {
|
||||
t.Fatalf("bad host or path (%q): want host=%s, got %s, path=%s, got %s", tt.root, tt.expectHost, ds.host, tt.expectPath, ds.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAvailable(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
remoteFile *RemoteFile
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
remoteFile: &RemoteFile{"1.2.3.4", "test", &mockClient{}, nil, nil},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
remoteFile: &RemoteFile{"1.2.3.4", "does-not-exist", &mockClient{}, nil, nil},
|
||||
expect: false,
|
||||
},
|
||||
} {
|
||||
if tt.remoteFile.IsAvailable() != tt.expect {
|
||||
t.Fatalf("expected remote file %s to be %v", tt.remoteFile.path, tt.expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchUserdata(t *testing.T) {
|
||||
rf := &RemoteFile{"1.2.3.4", "test", &mockClient{}, &mockReceiver{}, nil}
|
||||
b, _ := rf.FetchUserdata()
|
||||
|
||||
expect := []byte("cloud-config file")
|
||||
|
||||
if len(b) != len(expect) || !reflect.DeepEqual(b, expect) {
|
||||
t.Fatalf("expected length of buffer to be %d was %d. Expected %s, got %s", len(expect), len(b), string(expect), string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestType(t *testing.T) {
|
||||
rf := &RemoteFile{"1.2.3.4", "test", &mockClient{}, nil, nil}
|
||||
|
||||
if rf.Type() != "tftp" {
|
||||
t.Fatalf("expected remote file Type() to return %s got %s", "tftp", rf.Type())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user