netboot/pixiecore/booters.go

108 lines
2.7 KiB
Go

// Copyright 2016 Google Inc.
//
// 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 pixiecore
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
)
// StaticBooter boots all machines with the same Spec.
//
// IDs in spec should be either local file paths, or HTTP/HTTPS URLs.
func StaticBooter(spec *Spec) Booter {
ret := &staticBooter{
kernel: string(spec.Kernel),
spec: &Spec{
Kernel: "kernel",
Cmdline: map[string]interface{}{},
Message: spec.Message,
},
}
for i, initrd := range spec.Initrd {
ret.initrd = append(ret.initrd, string(initrd))
ret.spec.Initrd = append(ret.spec.Initrd, ID(fmt.Sprintf("initrd-%d", i)))
}
for k, v := range spec.Cmdline {
if id, ok := v.(ID); ok {
ret.otherIDs = append(ret.otherIDs, string(id))
ret.spec.Cmdline[k] = ID(fmt.Sprintf("other-%d", len(ret.otherIDs)-1))
} else {
ret.spec.Cmdline[k] = v
}
}
return ret
}
type staticBooter struct {
kernel string
initrd []string
otherIDs []string
spec *Spec
}
func (s *staticBooter) BootSpec(m Machine) (*Spec, error) {
return s.spec, nil
}
func (s *staticBooter) serveFile(path string) (io.ReadCloser, error) {
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
resp, err := http.Get(path)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("%s: %s", path, http.StatusText(resp.StatusCode))
}
return resp.Body, nil
}
return os.Open(path)
}
func (s *staticBooter) ReadBootFile(id ID) (io.ReadCloser, error) {
path := string(id)
switch {
case path == "kernel":
return s.serveFile(s.kernel)
case strings.HasPrefix(path, "initrd-"):
i, err := strconv.Atoi(string(path[7:]))
if err != nil || i < 0 || i >= len(s.initrd) {
return nil, fmt.Errorf("no file with ID %q", id)
}
return s.serveFile(s.initrd[i])
case strings.HasPrefix(path, "other-"):
i, err := strconv.Atoi(string(path[6:]))
if err != nil || i < 0 || i >= len(s.otherIDs) {
return nil, fmt.Errorf("no file with ID %q", id)
}
return s.serveFile(s.otherIDs[i])
}
return nil, fmt.Errorf("no file with ID %q", id)
}
func (s *staticBooter) WriteBootFile(ID, io.Reader) error {
return nil
}