refactor(*): address linter errors and warnings (#69)

This commit is contained in:
Andrew Rynhard 2018-05-08 17:40:43 -07:00 committed by GitHub
parent f6686bcc7b
commit 1115c86456
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 609 additions and 344 deletions

View File

@ -2,29 +2,51 @@ metadata:
repository: dianemo/initramfs
pipeline:
stages:
- proto
- cli
- generate
- build
stages:
proto:
build:
artifacts:
- source: /tmp/cli
destination: ../build/osctl
tasks:
- test
- cli
- init
generate:
artifacts:
- source: /go/src/github.com/autonomy/dianemo/proto
destination: ./src/init
tasks:
- proto
cli:
artifacts:
- source: /cli
destination: ../build/osctl
tasks:
- cli
build:
tasks:
- build
test:
tasks:
- test
- proto
tasks:
cli:
template: |
FROM dianemo/tools:{{ .Docker.Image.Tag }} AS {{ .Docker.CurrentStage }}
RUN ln -s /tools/lib64 /lib64
RUN mkdir /tmp
WORKDIR $GOPATH/src/github.com/autonomy/dianemo/initramfs/src/init
COPY src/init ./
WORKDIR $GOPATH/src/github.com/autonomy/dianemo/initramfs/src/init/cli
RUN GOOS=darwin go build -o /cli
RUN chmod +x /cli
init:
template: |
FROM dianemo/tools:{{ .Docker.Image.Tag }} AS {{ .Docker.CurrentStage }}
RUN ln -s /tools/lib64 /lib64
RUN mkdir /tmp
WORKDIR $GOPATH/src/github.com/autonomy/dianemo/initramfs/src/init
COPY src/init ./
RUN go build -ldflags '-s -w -linkmode external -extldflags "-static -L/usr/lib -lblkid -luuid"' -o /initramfs/init
RUN chmod +x /initramfs/init
WORKDIR /initramfs
RUN find . 2>/dev/null | cpio -H newc -o | xz -v -C crc32 -9 -e -T 0 -z >/initramfs/initramfs.xz
FROM scratch
WORKDIR /tmp
COPY --from=init /initramfs/init init
COPY --from=init /initramfs/initramfs.xz initramfs.xz
COPY --from=cli /cli cli
CMD false
proto:
template: |
FROM golang:1.10.0 AS {{ .Docker.CurrentStage }}
@ -38,26 +60,13 @@ tasks:
&& chmod +x /bin/protoc
COPY ./src/init/proto ./proto
RUN protoc -I/usr/local/include -I./proto --go_out=plugins=grpc:proto proto/api.proto
cli:
test:
template: |
FROM golang:1.10.0-alpine3.7 AS {{ .Docker.CurrentStage }}
RUN apk --update add gcc musl-dev util-linux-dev xz
FROM dianemo/tools:{{ .Docker.Image.Tag }} AS {{ .Docker.CurrentStage }}
RUN ln -s /tools/lib64 /lib64
RUN mkdir /tmp
RUN curl -L https://github.com/alecthomas/gometalinter/releases/download/v2.0.5/gometalinter-2.0.5-linux-amd64.tar.gz | tar -xz --strip-components=1 -C /tools/bin
WORKDIR $GOPATH/src/github.com/autonomy/dianemo/initramfs/src/init
COPY src/init ./
WORKDIR $GOPATH/src/github.com/autonomy/dianemo/initramfs/src/init/cli
RUN GOOS=darwin /usr/local/go/bin/go build -o /cli
RUN chmod +x /cli
build:
template: |
FROM golang:1.10.0-alpine3.7 AS {{ .Docker.CurrentStage }}
RUN apk --update add gcc musl-dev util-linux-dev xz
WORKDIR $GOPATH/src/github.com/autonomy/dianemo/initramfs/src/init
COPY src/init ./
RUN /usr/local/go/bin/go build -ldflags '-s -w -linkmode external -extldflags "-static -L/usr/lib -lblkid -luuid"' -o /initramfs/init
RUN chmod +x /initramfs/init
WORKDIR /initramfs
RUN find . 2>/dev/null | cpio -H newc -o | xz -v -C crc32 -9 -e -T 0 -z >/initramfs/initramfs.xz
FROM scratch
WORKDIR /tmp
COPY --from={{ .Docker.CurrentStage }} /initramfs/init init
COPY --from={{ .Docker.CurrentStage }} /initramfs/initramfs.xz initramfs.xz
COPY --from=autonomy/golang:1.10 /bin/test.sh /bin/test.sh
RUN test.sh

View File

@ -15,7 +15,10 @@ var dmesgCmd = &cobra.Command{
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 0 {
cmd.Usage()
if err := cmd.Usage(); err != nil {
// TODO: How should we handle this?
os.Exit(1)
}
os.Exit(1)
}
creds, err := client.NewDefaultClientCredentials()

View File

@ -24,6 +24,8 @@ var injectOSCmd = &cobra.Command{
Short: "Populates fields in the user data that are generated for the OS",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
var err error
if len(args) != 1 {
os.Exit(1)
}
@ -33,16 +35,16 @@ var injectOSCmd = &cobra.Command{
os.Exit(1)
}
data := &userdata.UserData{}
if err := yaml.Unmarshal(fileBytes, data); err != nil {
if err = yaml.Unmarshal(fileBytes, data); err != nil {
os.Exit(1)
}
if data.OS.Security == nil {
data.OS.Security = &userdata.Security{}
data.OS.Security.Identity = &userdata.KeyPair{}
data.OS.Security.CA = &userdata.KeyPair{}
data.OS.Security.Identity = &userdata.CertificateAndKeyPaths{}
data.OS.Security.CA = &userdata.CertificateAndKeyPaths{}
}
if identity != "" {
fileBytes, err := ioutil.ReadFile(identity + ".crt")
fileBytes, err = ioutil.ReadFile(identity + ".crt")
if err != nil {
os.Exit(1)
}
@ -54,7 +56,7 @@ var injectOSCmd = &cobra.Command{
data.OS.Security.Identity.Key = base64.StdEncoding.EncodeToString(fileBytes)
}
if ca != "" {
fileBytes, err := ioutil.ReadFile(ca + ".crt")
fileBytes, err = ioutil.ReadFile(ca + ".crt")
if err != nil {
os.Exit(1)
}
@ -65,7 +67,9 @@ var injectOSCmd = &cobra.Command{
if err != nil {
os.Exit(1)
}
ioutil.WriteFile(filename, dataBytes, 0700)
if err := ioutil.WriteFile(filename, dataBytes, 0700); err != nil {
os.Exit(1)
}
},
}
@ -85,15 +89,15 @@ var injectKubernetesCmd = &cobra.Command{
os.Exit(1)
}
data := &userdata.UserData{}
if err := yaml.Unmarshal(fileBytes, data); err != nil {
if err = yaml.Unmarshal(fileBytes, data); err != nil {
fmt.Println(err)
os.Exit(1)
}
if data.Kubernetes.CA == nil {
data.Kubernetes.CA = &userdata.KeyPair{}
data.Kubernetes.CA = &userdata.CertificateAndKeyPaths{}
}
if ca != "" {
fileBytes, err := ioutil.ReadFile(ca + ".crt")
fileBytes, err = ioutil.ReadFile(ca + ".crt")
if err != nil {
fmt.Println(err)
os.Exit(1)
@ -107,7 +111,7 @@ var injectKubernetesCmd = &cobra.Command{
data.Kubernetes.CA.Key = base64.StdEncoding.EncodeToString(fileBytes)
}
if hash != "" {
fileBytes, err := ioutil.ReadFile(hash + ".sha256")
fileBytes, err = ioutil.ReadFile(hash + ".sha256")
if err != nil {
fmt.Println(err)
os.Exit(1)
@ -119,7 +123,9 @@ var injectKubernetesCmd = &cobra.Command{
if err != nil {
os.Exit(1)
}
ioutil.WriteFile(filename, dataBytes, 0700)
if err := ioutil.WriteFile(filename, dataBytes, 0700); err != nil {
os.Exit(1)
}
},
}

View File

@ -24,7 +24,9 @@ var kubeconfigCmd = &cobra.Command{
if err != nil {
log.Fatal(err)
}
c.Kubeconfig()
if err := c.Kubeconfig(); err != nil {
os.Exit(1)
}
},
}

View File

@ -16,7 +16,9 @@ var logsCmd = &cobra.Command{
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cmd.Usage()
if err := cmd.Usage(); err != nil {
os.Exit(1)
}
os.Exit(1)
}
process := args[0]

View File

@ -10,7 +10,6 @@ import (
var (
address string
ca string
crt string
key string
isContainer bool
organization string

View File

@ -35,42 +35,45 @@ func hang() {
func init() {
log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds | log.Ltime)
os.Setenv("PATH", constants.PATH)
if err := os.Setenv("PATH", constants.PATH); err != nil {
panic(err)
}
switchRoot = flag.Bool("switch-root", false, "perform a switch_root")
flag.Parse()
}
func main() {
defer hang()
if !*switchRoot {
// Read the block devices and populate the mount point definitions.
if err := mount.Init(constants.NewRoot); err != nil {
panic(err)
}
// Download the user data.
data, err := userdata.Download()
if err != nil {
panic(err)
}
// Prepare the necessary files in the rootfs.
if err := rootfs.Prepare(constants.NewRoot, data); err != nil {
panic(err)
}
// Unmount the ROOT and DATA block devices
if err := mount.Unmount(); err != nil {
panic(err)
}
// Perform the equivalent of switch_root.
if err := switchroot.Switch(constants.NewRoot); err != nil {
panic(err)
}
func initram() (err error) {
// Read the block devices and populate the mount point definitions.
if err = mount.Init(constants.NewRoot); err != nil {
return
}
// Download the user data.
data, err := userdata.Download()
if err != nil {
panic(err)
return
}
// Prepare the necessary files in the rootfs.
if err = rootfs.Prepare(constants.NewRoot, data); err != nil {
return
}
// Unmount the ROOT and DATA block devices
if err = mount.Unmount(); err != nil {
return
}
// Perform the equivalent of switch_root.
if err = switchroot.Switch(constants.NewRoot); err != nil {
return
}
return nil
}
func root() (err error) {
// Download the user data.
data, err := userdata.Download()
if err != nil {
return
}
// Start the services essential to running Kubernetes.
@ -89,9 +92,19 @@ func main() {
services.Start(&service.Kubelet{})
services.Start(&service.Kubeadm{})
s, err := server.NewServer(data.OS.Security)
if err != nil {
return server.NewServer(data.OS.Security).Listen()
}
func main() {
defer hang()
if !*switchRoot {
if err := initram(); err != nil {
panic(err)
}
}
if err := root(); err != nil {
panic(err)
}
s.Listen()
}

View File

@ -1,3 +1,5 @@
// +build linux
// Package blkid provides bindings to libblkid.
package blkid
@ -14,6 +16,7 @@ import (
"unsafe"
)
// NewProbeFromFilename executes lblkid blkid_new_probe_from_filename.
func NewProbeFromFilename(s string) (C.blkid_probe, error) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
@ -25,12 +28,13 @@ func NewProbeFromFilename(s string) (C.blkid_probe, error) {
return pr, nil
}
// DoProbe executes lblkid blkid_do_probe.
func DoProbe(pr C.blkid_probe) {
C.blkid_do_probe(pr)
}
// ProbeLookupValue implements:
// int blkid_probe_lookup_value (blkid_probe pr, const char *name, const char **data, size_t *len);
// int blkid_probe_lookup_value (blkid_probe pr, const char *name, const char **data, size_t *len);
func ProbeLookupValue(pr C.blkid_probe, name string, size *int) (string, error) {
cs := C.CString(name)
defer C.free(unsafe.Pointer(cs))
@ -51,19 +55,19 @@ func ProbeLookupValue(pr C.blkid_probe, name string, size *int) (string, error)
}
// ProbeGetPartitions implements:
// blkid_partlist blkid_probe_get_partitions (blkid_probe pr);
// blkid_partlist blkid_probe_get_partitions (blkid_probe pr);
func ProbeGetPartitions(pr C.blkid_probe) C.blkid_partlist {
return C.blkid_probe_get_partitions(pr)
}
// ProbeGetPartitionsPartlistNumOfPartitions implements:
// int blkid_partlist_numof_partitions (blkid_partlist ls);
// int blkid_partlist_numof_partitions (blkid_partlist ls);
func ProbeGetPartitionsPartlistNumOfPartitions(ls C.blkid_partlist) int {
return int(C.blkid_partlist_numof_partitions(ls))
}
// FreeProbe implements:
// int blkid_partlist_numof_partitions (blkid_partlist ls);
// int blkid_partlist_numof_partitions (blkid_partlist ls);
func FreeProbe(pr C.blkid_probe) {
C.blkid_free_probe(pr)
}

View File

@ -7,31 +7,24 @@ import (
"os"
)
type Options struct {
Size int
}
type Option func(*Options)
// Chunker is an interface for embedding all chunking interfaces under one name.
type Chunker interface {
ChunkReader
}
// ChunkReader is an interface describing a reader that streams data in []byte
// chunks.
type ChunkReader interface {
Read(context.Context) <-chan []byte
}
// DefaultChunker is a conecrete type that implements the Chunker interface.
type DefaultChunker struct {
path string
options *Options
}
func Size(s int) Option {
return func(args *Options) {
args.Size = s
}
}
// NewDefaultChunker initializes a DefaultChunker with default values.
func NewDefaultChunker(path string, setters ...Option) Chunker {
opts := &Options{
Size: 1024,
@ -46,6 +39,22 @@ func NewDefaultChunker(path string, setters ...Option) Chunker {
}
}
// Options is the functional options struct.
type Options struct {
Size int
}
// Option is the functional option func.
type Option func(*Options)
// Size sets the chunk size of the Chunker.
func Size(s int) Option {
return func(args *Options) {
args.Size = s
}
}
// Read implements ChunkReader.
func (c *DefaultChunker) Read(ctx context.Context) <-chan []byte {
// Create a buffered channel of length 1.
ch := make(chan []byte, 1)
@ -56,13 +65,14 @@ func (c *DefaultChunker) Read(ctx context.Context) <-chan []byte {
go func(ch chan []byte, f *os.File) {
defer close(ch)
// nolint: errcheck
defer f.Close()
offset, err := f.Seek(0, io.SeekStart)
if err != nil {
return
}
buf := make([]byte, c.options.Size, c.options.Size)
buf := make([]byte, c.options.Size)
for {
select {
case <-ctx.Done():

View File

@ -16,25 +16,30 @@ import (
"google.golang.org/grpc/credentials"
)
type ClientCredentials struct {
// Credentials represents the set of values required to initialize a vaild
// Client.
type Credentials struct {
ca string
crt string
key string
}
// Client implements the proto.DianemoClient interface. It serves as the
// concrete type with the required methods.
type Client struct {
conn *grpc.ClientConn
client proto.DianemoClient
credentials *ClientCredentials
conn *grpc.ClientConn
client proto.DianemoClient
}
func NewDefaultClientCredentials() (creds *ClientCredentials, err error) {
// NewDefaultClientCredentials initializes ClientCredentials using default paths
// to the required CA, certificate, and key.
func NewDefaultClientCredentials() (creds *Credentials, err error) {
u, err := user.Current()
if err != nil {
return
}
creds = &ClientCredentials{
creds = &Credentials{
ca: path.Join(u.HomeDir, ".dianemo/ca.pem"),
crt: path.Join(u.HomeDir, ".dianemo/crt.pem"),
key: path.Join(u.HomeDir, ".dianemo/key.pem"),
@ -43,7 +48,8 @@ func NewDefaultClientCredentials() (creds *ClientCredentials, err error) {
return creds, nil
}
func NewClient(address string, port int, clientcreds *ClientCredentials) (c *Client, err error) {
// NewClient initializes a Client.
func NewClient(address string, port int, clientcreds *Credentials) (c *Client, err error) {
grpcOpts := []grpc.DialOption{}
caBytes, err := ioutil.ReadFile(clientcreds.ca)
@ -81,6 +87,7 @@ func NewClient(address string, port int, clientcreds *ClientCredentials) (c *Cli
return c, nil
}
// Kubeconfig implements the proto.DianemoClient interface.
func (c *Client) Kubeconfig() (err error) {
ctx := context.Background()
r, err := c.client.Kubeconfig(ctx, &empty.Empty{})
@ -92,6 +99,7 @@ func (c *Client) Kubeconfig() (err error) {
return nil
}
// Dmesg implements the proto.DianemoClient interface.
func (c *Client) Dmesg() (err error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@ -104,6 +112,7 @@ func (c *Client) Dmesg() (err error) {
return nil
}
// Logs implements the proto.DianemoClient interface.
func (c *Client) Logs(r *proto.LogsRequest) (err error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

View File

@ -1,19 +1,57 @@
package constants
const (
KernelRootFlag = "dianemo.autonomy.io/root"
UserDataURLFlag = "dianemo.autonomy.io/userdata"
NewRoot = "/root"
DATALabel = "DATA"
ROOTLabel = "ROOT"
PATH = "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/opt/cni/bin"
ContainerRuntimeDocker = "docker"
// KernelParamRoot is the kernel parameter name for specifying the root
// disk.
KernelParamRoot = "dianemo.autonomy.io/root"
// KernelParamUserData is the kernel parameter name for specifying the URL
// to the user data.
KernelParamUserData = "dianemo.autonomy.io/userdata"
// NewRoot is the path where the switchroot target is mounted.
NewRoot = "/root"
// DataPartitionLabel is the label of the partition to use for mounting at
// the data path.
DataPartitionLabel = "DATA"
// RootPartitionLabel is the label of the partition to use for mounting at
// the root path.
RootPartitionLabel = "ROOT"
// PATH defines all locations where executables are stored.
PATH = "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/opt/cni/bin"
// ContainerRuntimeDocker is the name of the Docker container runtime.
ContainerRuntimeDocker = "docker"
// ContainerRuntimeDockerSocket is the path to the Docker daemon socket.
ContainerRuntimeDockerSocket = "/var/run/docker.sock"
ContainerRuntimeCRIO = "crio"
ContainerRuntimeCRIOSocket = "/var/run/crio/crio.sock"
KubeadmConfig = "/etc/kubernetes/kubeadm.yaml"
KubeadmCACert = "/etc/kubernetes/pki/ca.crt"
KubeadmCAKey = "/etc/kubernetes/pki/ca.key"
SYSLOG_ACTION_SIZE_BUFFER = 10
SYSLOG_ACTION_READ_ALL = 3
// ContainerRuntimeCRIO is the name of the CRI-O container runtime.
ContainerRuntimeCRIO = "crio"
// ContainerRuntimeCRIOSocket is the path to the CRI-O daemon socket.
ContainerRuntimeCRIOSocket = "/var/run/crio/crio.sock"
// KubeadmConfig is the path to the kubeadm manifest file.
KubeadmConfig = "/etc/kubernetes/kubeadm.yaml"
// KubeadmCACert is the path to the root CA certificate.
KubeadmCACert = "/etc/kubernetes/pki/ca.crt"
// KubeadmCAKey is the path to the root CA private key.
KubeadmCAKey = "/etc/kubernetes/pki/ca.key"
)
// See https://linux.die.net/man/3/klogctl
const (
// SYSLOG_ACTION_SIZE_BUFFER is a named type argument to klogctl.
// nolint: golint
SYSLOG_ACTION_SIZE_BUFFER = 10
// SYSLOG_ACTION_READ_ALL is a named type argument to klogctl.
// nolint: golint
SYSLOG_ACTION_READ_ALL = 3
)

View File

@ -17,6 +17,7 @@ import (
"time"
)
// CertificateAuthority represents a CA.
type CertificateAuthority struct {
Crt *x509.Certificate
CrtPEM []byte
@ -24,25 +25,30 @@ type CertificateAuthority struct {
KeyPEM []byte
}
// Key represents an ECDSA private key.
type Key struct {
keyEC *ecdsa.PrivateKey
KeyPEM []byte
}
// Certificate represents an X.509 certificate.
type Certificate struct {
X509Certificate *x509.Certificate
X509CertificatePEM []byte
}
// CertificateSigningRequest represents a CSR.
type CertificateSigningRequest struct {
X509CertificateRequest *x509.CertificateRequest
X509CertificateRequestPEM []byte
}
// KeyPair represents a certificate and key pair.
type KeyPair struct {
*tls.Certificate
}
// Options is the functional options struct.
type Options struct {
Organization string
SignatureAlgorithm x509.SignatureAlgorithm
@ -52,44 +58,54 @@ type Options struct {
NotAfter time.Time
}
// Option is the functional option func.
type Option func(*Options)
// Organization sets the subject organization of the certificate.
func Organization(o string) Option {
return func(opts *Options) {
opts.Organization = o
}
}
// SignatureAlgorithm sets the hash algorithm used to sign the SSL certificate.
func SignatureAlgorithm(o x509.SignatureAlgorithm) Option {
return func(opts *Options) {
opts.SignatureAlgorithm = o
}
}
// IPAddresses sets the value for the IP addresses in Subject Alternate Name of
// the certificate.
func IPAddresses(o []net.IP) Option {
return func(opts *Options) {
opts.IPAddresses = o
}
}
// Bits sets the bit size of the RSA key pair.
func Bits(o int) Option {
return func(opts *Options) {
opts.Bits = o
}
}
// RSA sets a flag for indicating that the requested operation should be
// performed under the context of RSA instead of the default ECDSA.
func RSA(o bool) Option {
return func(opts *Options) {
opts.RSA = o
}
}
// NotAfter sets the validity bound describing when a certificate expires.
func NotAfter(o time.Time) Option {
return func(opts *Options) {
opts.NotAfter = o
}
}
// NewDefaultOptions initializes the Options struct with default values.
func NewDefaultOptions(setters ...Option) *Options {
opts := &Options{
SignatureAlgorithm: x509.ECDSAWithSHA512,
@ -106,6 +122,7 @@ func NewDefaultOptions(setters ...Option) *Options {
return opts
}
// NewSerialNumber generates a random serial number for an X.509 certificate.
func NewSerialNumber() (sn *big.Int, err error) {
snLimit := new(big.Int).Lsh(big.NewInt(1), 128)
sn, err = rand.Int(rand.Reader, snLimit)
@ -116,6 +133,8 @@ func NewSerialNumber() (sn *big.Int, err error) {
return sn, nil
}
// NewSelfSignedCertificateAuthority creates a self-signed CA configured for
// server and client authentication.
func NewSelfSignedCertificateAuthority(setters ...Option) (ca *CertificateAuthority, err error) {
opts := NewDefaultOptions(setters...)
@ -147,78 +166,12 @@ func NewSelfSignedCertificateAuthority(setters ...Option) (ca *CertificateAuthor
return rsaCertificateAuthority(crt, opts)
}
return ecdsaCertificateAuthority(crt, opts)
}
func rsaCertificateAuthority(template *x509.Certificate, opts *Options) (ca *CertificateAuthority, err error) {
key, e := rsa.GenerateKey(rand.Reader, opts.Bits)
if e != nil {
return
}
keyBytes := x509.MarshalPKCS1PrivateKey(key)
keyPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: keyBytes,
})
crtDER, e := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if e != nil {
return
}
crt, err := x509.ParseCertificate(crtDER)
if err != nil {
return
}
crtPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: crtDER,
})
ca = &CertificateAuthority{
Crt: crt,
CrtPEM: crtPEM,
Key: key,
KeyPEM: keyPEM,
}
return ca, nil
}
func ecdsaCertificateAuthority(template *x509.Certificate, opts *Options) (ca *CertificateAuthority, err error) {
key, e := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if e != nil {
return
}
keyBytes, e := x509.MarshalECPrivateKey(key)
if e != nil {
return
}
keyPEM := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: keyBytes,
})
crtDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if err != nil {
return
}
crt, err := x509.ParseCertificate(crtDER)
if err != nil {
return
}
crtPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: crtDER,
})
ca = &CertificateAuthority{
Crt: crt,
CrtPEM: crtPEM,
Key: key,
KeyPEM: keyPEM,
}
return ca, nil
return ecdsaCertificateAuthority(crt)
}
// NewCertificateSigningRequest creates a CSR. If the IPAddresses option is not
// specified, the CSR will be generated with the default value set in
// NewDefaultOptions.
func NewCertificateSigningRequest(key *ecdsa.PrivateKey, setters ...Option) (csr *CertificateSigningRequest, err error) {
opts := NewDefaultOptions(setters...)
@ -244,6 +197,7 @@ func NewCertificateSigningRequest(key *ecdsa.PrivateKey, setters ...Option) (csr
return csr, err
}
// NewKey generates an ECDSA private key.
func NewKey() (key *Key, err error) {
keyEC, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
@ -268,6 +222,8 @@ func NewKey() (key *Key, err error) {
return key, nil
}
// NewCertificateFromCSR creates and signs X.509 certificate using the provided
// CSR.
func NewCertificateFromCSR(ca *x509.Certificate, key *ecdsa.PrivateKey, csr *x509.CertificateRequest, setters ...Option) (crt *Certificate, err error) {
opts := NewDefaultOptions(setters...)
serialNumber, err := NewSerialNumber()
@ -318,6 +274,9 @@ func NewCertificateFromCSR(ca *x509.Certificate, key *ecdsa.PrivateKey, csr *x50
return crt, nil
}
// NewKeyPair generates a certificate signed by the provided CA, and an ECDSA
// private key. The certifcate and private key are then used to create an
// tls.X509KeyPair.
func NewKeyPair(ca *x509.Certificate, key *ecdsa.PrivateKey, setters ...Option) (keypair *KeyPair, err error) {
csr, err := NewCertificateSigningRequest(key, setters...)
if err != nil {
@ -345,10 +304,79 @@ func NewKeyPair(ca *x509.Certificate, key *ecdsa.PrivateKey, setters ...Option)
}
// Hash calculates the SHA-256 hash of the Subject Public Key Information (SPKI)
// object in an x509 certificate (in DER encoding). It returns the full hash as a
// hex encoded string (suitable for passing to Set.Allow).
// See https://github.com/kubernetes/kubernetes/blob/f557e0f7e3ee9089769ed3f03187fdd4acbb9ac1/cmd/kubeadm/app/util/pubkeypin/pubkeypin.go
// object in an x509 certificate (in DER encoding). It returns the full hash as
// a hex encoded string (suitable for passing to Set.Allow). See
// https://github.com/kubernetes/kubernetes/blob/f557e0f7e3ee9089769ed3f03187fdd4acbb9ac1/cmd/kubeadm/app/util/pubkeypin/pubkeypin.go
func Hash(crt *x509.Certificate) string {
spkiHash := sha256.Sum256(crt.RawSubjectPublicKeyInfo)
return "sha256" + ":" + strings.ToLower(hex.EncodeToString(spkiHash[:]))
}
func rsaCertificateAuthority(template *x509.Certificate, opts *Options) (ca *CertificateAuthority, err error) {
key, e := rsa.GenerateKey(rand.Reader, opts.Bits)
if e != nil {
return
}
keyBytes := x509.MarshalPKCS1PrivateKey(key)
keyPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: keyBytes,
})
crtDER, e := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if e != nil {
return
}
crt, err := x509.ParseCertificate(crtDER)
if err != nil {
return
}
crtPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: crtDER,
})
ca = &CertificateAuthority{
Crt: crt,
CrtPEM: crtPEM,
Key: key,
KeyPEM: keyPEM,
}
return ca, nil
}
func ecdsaCertificateAuthority(template *x509.Certificate) (ca *CertificateAuthority, err error) {
key, e := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if e != nil {
return
}
keyBytes, e := x509.MarshalECPrivateKey(key)
if e != nil {
return
}
keyPEM := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: keyBytes,
})
crtDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if err != nil {
return
}
crt, err := x509.ParseCertificate(crtDER)
if err != nil {
return
}
crtPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: crtDER,
})
ca = &CertificateAuthority{
Crt: crt,
CrtPEM: crtPEM,
Key: key,
KeyPEM: keyPEM,
}
return ca, nil
}

View File

@ -25,6 +25,7 @@ nameserver {{ $ip }}
{{ end }}
`
// Hosts renders a valid /etc/hosts file and writes it to disk.
func Hosts(s, hostname, ip string) error {
data := struct {
IP string
@ -52,6 +53,7 @@ func Hosts(s, hostname, ip string) error {
return nil
}
// ResolvConf renders a valid /etc/resolv.conf file and writes it to disk.
func ResolvConf(s string, userdata userdata.UserData) error {
tmpl, err := template.New("").Parse(resolvConfTemplate)
if err != nil {

View File

@ -5,6 +5,8 @@ import (
"strings"
)
// ParseProcCmdline parses /proc/cmdline and returns a map reprentation of the
// kernel parameters.
func ParseProcCmdline() (cmdline map[string]string, err error) {
cmdline = map[string]string{}
cmdlineBytes, err := ioutil.ReadFile("/proc/cmdline")

View File

@ -54,9 +54,7 @@ func Mount(s string) error {
// See https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
target = path.Join(s, "/sys/fs/cgroup", memoryCgroup, memoryUseHierarchy)
if err := ioutil.WriteFile(target, memoryUseHierarchyContents, memoryUseHierarchyPermissions); err != nil {
return err
}
err := ioutil.WriteFile(target, memoryUseHierarchyContents, memoryUseHierarchyPermissions)
return nil
return err
}

View File

@ -1,3 +1,5 @@
// +build linux
package mount
import (
@ -12,35 +14,15 @@ import (
"golang.org/x/sys/unix"
)
type (
// MountPoint represents a linux mount point.
MountPoint struct {
source string
target string
fstype string
flags uintptr
data string
}
// BlockDevice represents the metadata on a block device probed by
// libblkid.
BlockDevice struct {
dev string
TYPE string
UUID string
LABEL string
}
)
var (
instance struct {
special map[string]*MountPoint
blockdevices map[string]*MountPoint
special map[string]*Point
blockdevices map[string]*Point
}
once sync.Once
special = map[string]*MountPoint{
special = map[string]*Point{
"dev": {"devtmpfs", "/dev", "devtmpfs", unix.MS_NOSUID, "mode=0755"},
"proc": {"proc", "/proc", "proc", unix.MS_NOSUID | unix.MS_NOEXEC | unix.MS_NODEV, ""},
"sys": {"sysfs", "/sys", "sysfs", unix.MS_NOSUID | unix.MS_NOEXEC | unix.MS_NODEV, ""},
@ -49,15 +31,32 @@ var (
}
)
// Point represents a linux mount point.
type Point struct {
source string
target string
fstype string
flags uintptr
data string
}
// BlockDevice represents the metadata on a block device probed by libblkid.
type BlockDevice struct {
dev string
TYPE string
UUID string
LABEL string
}
// Init initializes the mount points.
func Init(s string) error {
once.Do(func() {
instance = struct {
special map[string]*MountPoint
blockdevices map[string]*MountPoint
special map[string]*Point
blockdevices map[string]*Point
}{
special,
map[string]*MountPoint{},
map[string]*Point{},
}
})
@ -75,16 +74,16 @@ func Init(s string) error {
return fmt.Errorf("probe block devices: %s", err.Error())
}
for _, b := range probed {
mountpoint := &MountPoint{
mountpoint := &Point{
source: b.dev,
fstype: b.TYPE,
flags: unix.MS_NOATIME,
data: "",
}
switch b.LABEL {
case constants.ROOTLabel:
case constants.RootPartitionLabel:
mountpoint.target = s
case constants.DATALabel:
case constants.DataPartitionLabel:
mountpoint.target = path.Join(s, "var")
}
@ -114,7 +113,7 @@ func Move(s string) error {
return fmt.Errorf("move mount point %s to %s: %s", mountpoint.target, target, err.Error())
}
if label == "dev" {
mountpoint = &MountPoint{"devpts", path.Join(s, "/dev/pts"), "devpts", unix.MS_NOSUID | unix.MS_NOEXEC, "ptmxmode=000,mode=620,gid=5"}
mountpoint = &Point{"devpts", path.Join(s, "/dev/pts"), "devpts", unix.MS_NOSUID | unix.MS_NOEXEC, "ptmxmode=000,mode=620,gid=5"}
if err := os.MkdirAll(mountpoint.target, os.ModeDir); err != nil {
return fmt.Errorf("create %s: %s", mountpoint.target, err.Error())
}
@ -129,11 +128,7 @@ func Move(s string) error {
// Finalize moves the mount points created in Init, to the new root.
func Finalize(s string) error {
if err := unix.Mount(s, "/", "", unix.MS_MOVE, ""); err != nil {
return err
}
return nil
return unix.Mount(s, "/", "", unix.MS_MOVE, "")
}
// Mount moves the mount points created in Init, to the new root.
@ -142,7 +137,7 @@ func Mount(s string) error {
return err
}
mountpoint, ok := instance.blockdevices[constants.ROOTLabel]
mountpoint, ok := instance.blockdevices[constants.RootPartitionLabel]
if ok {
mountpoint.flags = unix.MS_RDONLY | unix.MS_NOATIME
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
@ -163,7 +158,7 @@ func Mount(s string) error {
return fmt.Errorf("mount %s as shared: %s", mountpoint.target, err.Error())
}
}
mountpoint, ok = instance.blockdevices[constants.DATALabel]
mountpoint, ok = instance.blockdevices[constants.DataPartitionLabel]
if ok {
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
@ -175,13 +170,13 @@ func Mount(s string) error {
// Unmount unmounts the ROOT and DATA block devices.
func Unmount() error {
mountpoint, ok := instance.blockdevices[constants.DATALabel]
mountpoint, ok := instance.blockdevices[constants.DataPartitionLabel]
if ok {
if err := unix.Unmount(mountpoint.target, 0); err != nil {
return fmt.Errorf("unmount mount point %s: %s", mountpoint.target, err.Error())
}
}
mountpoint, ok = instance.blockdevices[constants.ROOTLabel]
mountpoint, ok = instance.blockdevices[constants.RootPartitionLabel]
if ok {
if err := unix.Unmount(mountpoint.target, 0); err != nil {
return fmt.Errorf("unmount mount point %s: %s", mountpoint.target, err.Error())
@ -199,7 +194,7 @@ func probe() (b []*BlockDevice, err error) {
return
}
if root, ok := arguments[constants.KernelRootFlag]; ok {
if root, ok := arguments[constants.KernelParamRoot]; ok {
if _, err := os.Stat(root); os.IsNotExist(err) {
return nil, fmt.Errorf("device does not exist: %s", root)
}

View File

@ -24,6 +24,7 @@ func ip() string {
return ""
}
// Prepare creates the files required by the installed binaries and libraries.
func Prepare(s string, userdata userdata.UserData) error {
// Create /etc/hosts
hostname, err := os.Hostname()

View File

@ -1,3 +1,5 @@
//+build linux
package server
import (
@ -15,7 +17,7 @@ import (
"github.com/autonomy/dianemo/initramfs/src/init/pkg/constants"
servicelog "github.com/autonomy/dianemo/initramfs/src/init/pkg/service/log"
"github.com/autonomy/dianemo/initramfs/src/init/pkg/userdata"
proto "github.com/autonomy/dianemo/initramfs/src/init/proto"
"github.com/autonomy/dianemo/initramfs/src/init/proto"
"github.com/golang/protobuf/ptypes/empty"
"github.com/kubernetes-incubator/cri-o/client"
"golang.org/x/sys/unix"
@ -23,6 +25,8 @@ import (
"google.golang.org/grpc/credentials"
)
// Server implements the proto.DianemoServer interface. It serves as the
// concrete type with the required methods.
type Server struct {
server *grpc.Server
port int
@ -31,16 +35,23 @@ type Server struct {
key string
}
func NewServer(data *userdata.Security) (s *Server, err error) {
// NewServer initializes a Server.
func NewServer(data *userdata.Security) (s *Server) {
s = &Server{
port: 50000,
ca: data.CA.Crt,
crt: data.Identity.Crt,
key: data.Identity.Key,
}
return s, err
return s
}
// Listen configures TLS for mutual authtentication by loading the CA into a
// CertPool and configuring the server's policy for TLS Client Authentication.
// Once TLS is configured, the gRPC options are built to make use of the TLS
// configuration and the receiver (Server) is registered to the gRPC server.
// Finally the gRPC server is started.
func (s *Server) Listen() (err error) {
var (
listener net.Listener
@ -97,25 +108,34 @@ func (s *Server) Listen() (err error) {
return
}
return
return err
}
func (s *Server) Kubeconfig(ctx context.Context, in *empty.Empty) (r *proto.Data, err error) {
// Kubeconfig implements the proto.DianemoServer interface. The admin kubeconfig
// is generated by kubeadm and placed at /etc/kubernetes/admin.conf. This method
// returns the contents of the generated admin.conf and returns it in the
// response.
func (s *Server) Kubeconfig(ctx context.Context, in *empty.Empty) (data *proto.Data, err error) {
fileBytes, err := ioutil.ReadFile("/etc/kubernetes/admin.conf")
if err != nil {
return
}
r = &proto.Data{
data = &proto.Data{
Bytes: fileBytes,
}
return
return data, err
}
// Processes implements the proto.DianemoServer interface.
func (s *Server) Processes(ctx context.Context, in *proto.ProcessesRequest) (r *proto.ProcessesReply, err error) {
return
}
// Dmesg implements the proto.DianemoServer interface. The klogctl syscall is
// used to read from the ring buffer at /proc/kmsg by taking the
// SYSLOG_ACTION_READ_ALL action. This action reads all messages remaining in
// the ring buffer non-destructively.
func (s *Server) Dmesg(ctx context.Context, in *empty.Empty) (data *proto.Data, err error) {
// Return the size of the kernel ring buffer
size, err := unix.Klogctl(constants.SYSLOG_ACTION_SIZE_BUFFER, nil)
@ -131,11 +151,13 @@ func (s *Server) Dmesg(ctx context.Context, in *empty.Empty) (data *proto.Data,
data = &proto.Data{Bytes: buf[:n]}
return
return data, err
}
// Logs implements the proto.DianemoServer interface. Service or container logs
// can be requested and the contents of the log file are streamed in chunks.
func (s *Server) Logs(r *proto.LogsRequest, l proto.Dianemo_LogsServer) (err error) {
var stream chunker.ChunkReader
var chunk chunker.ChunkReader
if r.Container {
// TODO: Use the specified container runtime.
cli, e := client.New("/var/run/crio/crio.sock")
@ -148,18 +170,20 @@ func (s *Server) Logs(r *proto.LogsRequest, l proto.Dianemo_LogsServer) (err err
err = e
return
}
stream = chunker.NewDefaultChunker(info.LogPath)
chunk = chunker.NewDefaultChunker(info.LogPath)
} else {
stream = servicelog.Get(r.Process)
if stream == nil {
chunk = servicelog.Chunker(r.Process)
if chunk == nil {
err = fmt.Errorf("no such process: %s", r.Process)
return
}
}
for data := range stream.Read(l.Context()) {
l.Send(&proto.Data{Bytes: data})
for data := range chunk.Read(l.Context()) {
if err = l.Send(&proto.Data{Bytes: data}); err != nil {
return
}
}
return
return err
}

View File

@ -5,12 +5,15 @@ import (
"time"
)
// None is a service condition that has no conditions.
func None() func() (bool, error) {
return func() (bool, error) {
return true, nil
}
}
// FileExists is a service condition that checks for the existence of a file
// once and only once.
func FileExists(file string) func() (bool, error) {
return func() (bool, error) {
_, err := os.Stat(file)
@ -26,6 +29,8 @@ func FileExists(file string) func() (bool, error) {
}
}
// WaitForFileExists is a service condition that will wait for the existence of
// a file.
func WaitForFileExists(file string) func() (bool, error) {
return func() (bool, error) {
for {

View File

@ -180,8 +180,11 @@ const crioPolicy = `
}
`
// CRIO implements the Service interface. It serves as the concrete type with
// the required methods.
type CRIO struct{}
// Pre implements the Service interface.
func (p *CRIO) Pre(data userdata.UserData) error {
if err := ioutil.WriteFile("/etc/crio/crio.conf", []byte(crioConf), 0644); err != nil {
return fmt.Errorf("write crio.conf: %s", err.Error())
@ -193,6 +196,7 @@ func (p *CRIO) Pre(data userdata.UserData) error {
return nil
}
// Cmd implements the Service interface.
func (p *CRIO) Cmd(data userdata.UserData) (name string, args []string) {
name = "/bin/crio"
args = []string{}
@ -200,10 +204,13 @@ func (p *CRIO) Cmd(data userdata.UserData) (name string, args []string) {
return name, args
}
// Condition implements the Service interface.
func (p *CRIO) Condition(data userdata.UserData) func() (bool, error) {
return conditions.None()
}
// Env implements the Service interface.
func (p *CRIO) Env() []string { return []string{} }
// Type implements the Service interface.
func (p *CRIO) Type() Type { return Forever }

View File

@ -5,12 +5,16 @@ import (
"github.com/autonomy/dianemo/initramfs/src/init/pkg/userdata"
)
// Docker implements the Service interface. It serves as the concrete type with
// the required methods.
type Docker struct{}
// Pre implements the Service interface.
func (p *Docker) Pre(data userdata.UserData) error {
return nil
}
// Cmd implements the Service interface.
func (p *Docker) Cmd(data userdata.UserData) (name string, args []string) {
name = "/bin/dockerd"
args = []string{
@ -27,12 +31,15 @@ func (p *Docker) Cmd(data userdata.UserData) (name string, args []string) {
return name, args
}
// Condition implements the Service interface.
func (p *Docker) Condition(data userdata.UserData) func() (bool, error) {
return conditions.None()
}
// Env implements the Service interface.
func (p *Docker) Env() []string {
return []string{"DOCKER_NOFILE=1000000"}
}
// Type implements the Service interface.
func (p *Docker) Type() Type { return Forever }

View File

@ -14,6 +14,7 @@ import (
"github.com/autonomy/dianemo/initramfs/src/init/pkg/userdata"
)
// MasterConfiguration is the kubeadm manifest for master nodes.
const MasterConfiguration = `
kind: MasterConfiguration
apiVersion: kubeadm.k8s.io/v1alpha1
@ -36,6 +37,7 @@ featureGates:
CoreDNS: true
`
// NodeConfiguration is the kubeadm manifest for worker nodes.
const NodeConfiguration = `
kind: NodeConfiguration
apiVersion: kubeadm.k8s.io/v1alpha1
@ -50,11 +52,12 @@ criSocket: {{ .CRISocket }}
nodeName: {{ .NodeName }}
`
// Kubeadm implements the Service interface. It serves as the concrete type with
// the required methods.
type Kubeadm struct{}
var cmd string
func (p *Kubeadm) Pre(data userdata.UserData) error {
// Pre implements the Service interface.
func (p *Kubeadm) Pre(data userdata.UserData) (err error) {
var configuration string
if data.Kubernetes.Join {
configuration = NodeConfiguration
@ -62,63 +65,28 @@ func (p *Kubeadm) Pre(data userdata.UserData) error {
configuration = MasterConfiguration
}
var criSocket string
var socket string
switch data.Kubernetes.ContainerRuntime {
case constants.ContainerRuntimeDocker:
criSocket = constants.ContainerRuntimeDockerSocket
socket = constants.ContainerRuntimeDockerSocket
case constants.ContainerRuntimeCRIO:
criSocket = constants.ContainerRuntimeCRIOSocket
socket = constants.ContainerRuntimeCRIOSocket
}
aux := struct {
*userdata.Kubernetes
CRISocket string
}{
data.Kubernetes,
criSocket,
}
tmpl, err := template.New("").Parse(configuration)
if err != nil {
return err
}
var buf []byte
writer := bytes.NewBuffer(buf)
err = tmpl.Execute(writer, aux)
if err != nil {
return err
}
if err := ioutil.WriteFile(constants.KubeadmConfig, writer.Bytes(), 0400); err != nil {
return fmt.Errorf("write %s: %s", constants.KubeadmConfig, err.Error())
if err = writeKubeadmManifest(data.Kubernetes, configuration, socket); err != nil {
return
}
if !data.Kubernetes.Join {
caCrtBytes, err := base64.StdEncoding.DecodeString(data.Kubernetes.CA.Crt)
if err != nil {
return err
}
if err := os.MkdirAll(path.Dir(constants.KubeadmCACert), 0600); err != nil {
return err
}
if err := ioutil.WriteFile(constants.KubeadmCACert, caCrtBytes, 0400); err != nil {
return fmt.Errorf("write %s: %s", constants.KubeadmCACert, err.Error())
}
caKeyBytes, err := base64.StdEncoding.DecodeString(data.Kubernetes.CA.Key)
if err != nil {
return err
}
if err := os.MkdirAll(path.Dir(constants.KubeadmCAKey), 0600); err != nil {
return err
}
if err := ioutil.WriteFile(constants.KubeadmCAKey, caKeyBytes, 0400); err != nil {
return fmt.Errorf("write %s: %s", constants.KubeadmCAKey, err.Error())
if err = writeKubeadmPKIFiles(data.Kubernetes); err != nil {
return
}
}
return nil
}
// Cmd implements the Service interface.
func (p *Kubeadm) Cmd(data userdata.UserData) (name string, args []string) {
var cmd string
if data.Kubernetes.Join {
@ -136,6 +104,7 @@ func (p *Kubeadm) Cmd(data userdata.UserData) (name string, args []string) {
return name, args
}
// Condition implements the Service interface.
func (p *Kubeadm) Condition(data userdata.UserData) func() (bool, error) {
switch data.Kubernetes.ContainerRuntime {
case constants.ContainerRuntimeDocker:
@ -147,6 +116,61 @@ func (p *Kubeadm) Condition(data userdata.UserData) func() (bool, error) {
}
}
// Env implements the Service interface.
func (p *Kubeadm) Env() []string { return []string{} }
// Type implements the Service interface.
func (p *Kubeadm) Type() Type { return Once }
func writeKubeadmManifest(data *userdata.Kubernetes, configuration, socket string) (err error) {
aux := struct {
*userdata.Kubernetes
CRISocket string
}{
data,
socket,
}
tmpl, err := template.New("").Parse(configuration)
if err != nil {
return err
}
var buf []byte
writer := bytes.NewBuffer(buf)
err = tmpl.Execute(writer, aux)
if err != nil {
return err
}
if err = ioutil.WriteFile(constants.KubeadmConfig, writer.Bytes(), 0400); err != nil {
return fmt.Errorf("write %s: %s", constants.KubeadmConfig, err.Error())
}
return nil
}
func writeKubeadmPKIFiles(data *userdata.Kubernetes) (err error) {
caCrtBytes, err := base64.StdEncoding.DecodeString(data.CA.Crt)
if err != nil {
return err
}
if err = os.MkdirAll(path.Dir(constants.KubeadmCACert), 0600); err != nil {
return err
}
if err = ioutil.WriteFile(constants.KubeadmCACert, caCrtBytes, 0400); err != nil {
return fmt.Errorf("write %s: %s", constants.KubeadmCACert, err.Error())
}
caKeyBytes, err := base64.StdEncoding.DecodeString(data.CA.Key)
if err != nil {
return err
}
if err = os.MkdirAll(path.Dir(constants.KubeadmCAKey), 0600); err != nil {
return err
}
if err = ioutil.WriteFile(constants.KubeadmCAKey, caKeyBytes, 0400); err != nil {
return fmt.Errorf("write %s: %s", constants.KubeadmCAKey, err.Error())
}
return nil
}

View File

@ -9,8 +9,11 @@ import (
"github.com/autonomy/dianemo/initramfs/src/init/pkg/userdata"
)
// Kubelet implements the Service interface. It serves as the concrete type with
// the required methods.
type Kubelet struct{}
// Pre implements the Service interface.
func (p *Kubelet) Pre(data userdata.UserData) error {
if err := os.Mkdir("/run/flannel", os.ModeDir); err != nil {
return fmt.Errorf("create /run/flannel: %s", err.Error())
@ -25,6 +28,7 @@ func (p *Kubelet) Pre(data userdata.UserData) error {
return nil
}
// Cmd implements the Service interface.
func (p *Kubelet) Cmd(data userdata.UserData) (name string, args []string) {
name = "/bin/kubelet"
args = []string{
@ -65,6 +69,7 @@ func (p *Kubelet) Cmd(data userdata.UserData) (name string, args []string) {
return name, args
}
// Condition implements the Service interface.
func (p *Kubelet) Condition(data userdata.UserData) func() (bool, error) {
switch data.Kubernetes.ContainerRuntime {
case constants.ContainerRuntimeDocker:
@ -76,6 +81,8 @@ func (p *Kubelet) Condition(data userdata.UserData) func() (bool, error) {
}
}
// Env implements the Service interface.
func (p *Kubelet) Env() []string { return []string{} }
// Type implements the Service interface.
func (p *Kubelet) Type() Type { return Forever }

View File

@ -14,12 +14,15 @@ import (
var instance = map[string]*Log{}
var mu = &sync.Mutex{}
// Log represents the log of a service. It supports streaming of the contents of
// the log file by way of implementing the chunker.Chunker interface.
type Log struct {
Name string
Path string
writeCloser io.WriteCloser
}
// New initializes and registers a log for a service.
func New(name string) (*Log, error) {
if l, ok := instance[name]; ok {
return l, nil
@ -43,7 +46,8 @@ func New(name string) (*Log, error) {
return l, nil
}
func Get(name string) chunker.Chunker {
// Chunker returns a chunker.Chunker implementation.
func Chunker(name string) chunker.Chunker {
if l, ok := instance[name]; ok {
return l
}
@ -51,14 +55,17 @@ func Get(name string) chunker.Chunker {
return nil
}
// Write implements io.WriteCloser.
func (l *Log) Write(p []byte) (n int, err error) {
return l.writeCloser.Write(p)
}
// Close implements io.WriteCloser.
func (l *Log) Close() error {
return l.writeCloser.Close()
}
// Read implements chunker.Chunker.
func (l *Log) Read(ctx context.Context) <-chan []byte {
c := chunker.NewDefaultChunker(l.Path)
return c.Read(ctx)

View File

@ -12,34 +12,53 @@ import (
"github.com/autonomy/dianemo/initramfs/src/init/pkg/userdata"
)
// Type represents the service's restart policy.
type Type int
const (
// Forever will always restart a process.
Forever Type = iota
// Once will restart the process only if it did not exit successfully.
Once
OnceAndOnlyOnce
)
// Service is an interface describing a process that is to be run as a system
// level service.
type Service interface {
// Pre is invoked before a command is executed. It is useful for things like
// preparing files that the process might depend on.
Pre(userdata.UserData) error
// Cmd describes the path to the binary, and the set of arguments to be
// passed into it upon execution.
Cmd(userdata.UserData) (string, []string)
// Condition is invoked just before starting the process.
Condition(userdata.UserData) func() (bool, error)
// Env describes the service's environment variables. Elements should be in
// the format <key=<value>
Env() []string
// Type describes the service's restart policy.
Type() Type
}
// Manager is a type with helper methods that build a service and invoke the set
// of methods defined in the Service interface.
type Manager struct {
UserData userdata.UserData
}
func (m *Manager) build(proc Service) (*exec.Cmd, error) {
func (m *Manager) build(proc Service) (cmd *exec.Cmd, err error) {
// Build the exec.Cmd
name, args := proc.Cmd(m.UserData)
cmd := exec.Command(name, args...)
cmd = exec.Command(name, args...)
// Set the environment for the service.
cmd.Env = append(proc.Env(), fmt.Sprintf("PATH=%s", constants.PATH))
// Setup logging.
w, err := servicelog.New(path.Base(name))
if err != nil {
return nil, fmt.Errorf("service log handler: %s", err.Error())
err = fmt.Errorf("service log handler: %s", err.Error())
return
}
cmd.Stdout = w
cmd.Stderr = w
@ -47,6 +66,9 @@ func (m *Manager) build(proc Service) (*exec.Cmd, error) {
return cmd, nil
}
// Start will invoke the service's Pre, Condition, and Type funcs. If the any
// error occurs in the Pre or Condition invocations, it is up to the caller to
// to restart the service.
func (m *Manager) Start(proc Service) {
go func(proc Service) {
err := proc.Pre(m.UserData)
@ -63,7 +85,7 @@ func (m *Manager) Start(proc Service) {
return
}
// Wait for the command to exit. Then, based on the service Type, take
// the appropriate actions.
// the requested action.
switch proc.Type() {
case Forever:
if err := m.waitAndRestart(proc); err != nil {
@ -77,17 +99,17 @@ func (m *Manager) Start(proc Service) {
}(proc)
}
func (m *Manager) waitAndRestart(proc Service) error {
func (m *Manager) waitAndRestart(proc Service) (err error) {
cmd, err := m.build(proc)
if err != nil {
return err
return
}
if err := cmd.Start(); err != nil {
return err
if err = cmd.Start(); err != nil {
return
}
state, err := cmd.Process.Wait()
if err != nil {
// TODO: Write the error to the log writer.
return
}
if state.Exited() {
time.Sleep(5 * time.Second)
@ -97,17 +119,17 @@ func (m *Manager) waitAndRestart(proc Service) error {
return nil
}
func (m *Manager) waitForSuccess(proc Service) error {
func (m *Manager) waitForSuccess(proc Service) (err error) {
cmd, err := m.build(proc)
if err != nil {
return err
return
}
if err := cmd.Start(); err != nil {
return err
if err = cmd.Start(); err != nil {
return
}
state, err := cmd.Process.Wait()
if err != nil {
// TODO: Write the error to the log writer.
return
}
if !state.Success() {
time.Sleep(5 * time.Second)

View File

@ -1,3 +1,5 @@
// +build linux
package switchroot
import (
@ -16,9 +18,10 @@ func recursiveDelete(fd int) error {
return err
}
// The file descriptor is already open, but allocating a os.File
// here makes reading the files in the dir so much nicer.
// The file descriptor is already open, but allocating a os.File here makes
// reading the files in the dir so much nicer.
dir := os.NewFile(uintptr(fd), "__ignored__")
// nolint: errcheck
defer dir.Close()
names, err := dir.Readdirnames(-1)
if err != nil {
@ -26,7 +29,8 @@ func recursiveDelete(fd int) error {
}
for _, name := range names {
// Loop here, but handle loop in separate function to make defer work as expected.
// Loop here, but handle loop in separate function to make defer work as
// expected.
if err := recusiveDeleteInner(fd, parentDev, name); err != nil {
return err
}
@ -35,8 +39,9 @@ func recursiveDelete(fd int) error {
}
func recusiveDeleteInner(parentFd int, parentDev uint64, childName string) error {
// O_DIRECTORY and O_NOFOLLOW make this open fail for all files and all symlinks (even when pointing to a dir).
// We need to filter out symlinks because getDev later follows them.
// O_DIRECTORY and O_NOFOLLOW make this open fail for all files and all
// symlinks (even when pointing to a dir). We need to filter out symlinks
// because getDev later follows them.
childFd, err := unix.Openat(parentFd, childName, unix.O_DIRECTORY|unix.O_NOFOLLOW, unix.O_RDWR)
if err != nil {
// childName points to either a file or a symlink, delete in any case.
@ -45,6 +50,7 @@ func recusiveDeleteInner(parentFd int, parentDev uint64, childName string) error
}
} else {
// Open succeeded, which means childName points to a real directory.
// nolint: errcheck
defer unix.Close(childFd)
// Don't descent into other file systems.
@ -76,7 +82,8 @@ func getDev(fd int) (dev uint64, err error) {
return stat.Dev, nil
}
// See https://github.com/karelzak/util-linux/blob/master/sys-utils/switch_root.c
// Switch performs a switch_root equivalent. See
// https://github.com/karelzak/util-linux/blob/master/sys-utils/switch_root.c
func Switch(s string) error {
// Mount the ROOT and DATA block devices at the new root.
if err := mount.Mount(s); err != nil {
@ -97,6 +104,7 @@ func Switch(s string) error {
if err != nil {
return err
}
// nolint: errcheck
defer oldRoot.Close()
if err := mount.Finalize(s); err != nil {
return err

View File

@ -17,37 +17,45 @@ type UserData struct {
Kubernetes *Kubernetes `yaml:"kubernetes,omitempty"`
}
// OS represents the operating system specific configuration options.
type OS struct {
Network *Network `yaml:"network,omitempty"`
Security *Security `yaml:"security"`
}
// Network represents the operating system networking specific configuration
// options.
type Network struct {
Nameservers []string `yaml:"nameservers,omitempty"`
}
// Security represents the operating system security specific configuration
// options.
type Security struct {
CA *KeyPair `yaml:"ca"`
Identity *KeyPair `yaml:"identity"`
CA *CertificateAndKeyPaths `yaml:"ca"`
Identity *CertificateAndKeyPaths `yaml:"identity"`
}
type KeyPair struct {
// CertificateAndKeyPaths represents the paths to the certificate and private
// key.
type CertificateAndKeyPaths struct {
Crt string `yaml:"crt"`
Key string `yaml:"key"`
}
// Kubernetes represents the Kubernetes specific configuration options.
type Kubernetes struct {
CA *KeyPair `yaml:"ca,omitempty"`
Token string `yaml:"token"`
Join bool `yaml:"join,omitempty"`
APIServer string `yaml:"apiServer,omitempty"`
NodeName string `yaml:"nodeName,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
ContainerRuntime string `yaml:"containerRuntime,omitempty"`
DiscoveryTokenCACertHashes []string `yaml:"discoveryTokenCACertHashes,omitempty"`
CA *CertificateAndKeyPaths `yaml:"ca,omitempty"`
Token string `yaml:"token"`
Join bool `yaml:"join,omitempty"`
APIServer string `yaml:"apiServer,omitempty"`
NodeName string `yaml:"nodeName,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
ContainerRuntime string `yaml:"containerRuntime,omitempty"`
DiscoveryTokenCACertHashes []string `yaml:"discoveryTokenCACertHashes,omitempty"`
}
// Download downloads the user data and executes the instructions.
// Download initializes a UserData struct from a remote URL.
func Download() (UserData, error) {
userData := UserData{}
@ -55,7 +63,7 @@ func Download() (UserData, error) {
if err != nil {
return userData, fmt.Errorf("parse kernel parameters: %s", err.Error())
}
url, ok := arguments[constants.UserDataURLFlag]
url, ok := arguments[constants.KernelParamUserData]
if !ok {
return userData, nil
}
@ -64,6 +72,7 @@ func Download() (UserData, error) {
if err != nil {
return userData, err
}
// nolint: errcheck
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {

View File

@ -17,7 +17,6 @@ metadata:
srcFindutils: http://ftp.gnu.org/gnu/findutils/findutils-4.6.0.tar.gz
srcGlib2: http://ftp.gnome.org/pub/gnome/sources/glib/2.56/glib-2.56.0.tar.xz
srcGlibc: http://ftp.gnu.org/gnu/glibc/glibc-2.26.tar.xz
srcGo: https://dl.google.com/go/go1.10.linux-amd64.tar.gz
srcGPGME: https://www.gnupg.org/ftp/gcrypt/gpgme/gpgme-1.10.0.tar.bz2
srcIproute2: https://www.kernel.org/pub/linux/utils/net/iproute2/iproute2-4.15.0.tar.xz
srcKubeadm: https://storage.googleapis.com/kubernetes-release/release/v1.10.2/bin/linux/amd64/kubeadm
@ -79,15 +78,11 @@ stages:
- docker
- kubernetes
- etc
- cleanup
rootfs:
tasks:
- rootfs
tasks:
docker:
template: |
WORKDIR {{ index .Variables "rootfs" }}/bin
RUN curl -L {{ index .Variables "srcDocker" }} | tar --strip-components=1 -xz \
&& rm docker
btrfs-progs:
template: |
WORKDIR {{ index .Variables "dest" }}/{{ .Docker.CurrentStage }}
@ -104,6 +99,11 @@ tasks:
template: |
RUN mkdir -p {{ index .Variables "rootfs" }}/etc/ssl/certs
RUN curl -o {{ index .Variables "rootfs" }}/etc/ssl/certs/ca-certificates.crt https://curl.haxx.se/ca/cacert.pem
cleanup:
template: |
COPY src/cleanup.sh /bin/cleanup.sh
RUN chmod +x /bin/cleanup.sh
RUN /bin/cleanup.sh
conntrack-tools:
template: |
WORKDIR {{ index .Variables "dest" }}/{{ .Docker.CurrentStage }}
@ -129,9 +129,9 @@ tasks:
cri-o:
template: |
WORKDIR {{ index .Variables "dest" }}/{{ .Docker.CurrentStage }}
RUN curl -L {{ index .Variables "srcGo" }} | tar -C /tools/usr/local -xz
ENV PATH $PATH:/tools/usr/local/go/bin
ENV GOPATH {{ index .Variables "dest" }}
ENV GOROOT /tools/usr/local/go
ENV GOPATH /tools/go
ENV PATH $PATH:$GOROOT/bin:$GOPATH/bin
RUN curl -L {{ index .Variables "srcCRITools" }} | tar -xz -C {{ index .Variables "rootfs" }}/bin
RUN curl -L {{ index .Variables "srcCRIO" }} | tar --strip-components=1 -xz
RUN ln -s {{ index .Variables "rootfs" }}/etc/ssl /etc/ssl
@ -139,6 +139,11 @@ tasks:
RUN mkdir bin
RUN make -j $(($(nproc) / 2)) BUILDTAGS=""
RUN make install install.config
docker:
template: |
WORKDIR {{ index .Variables "rootfs" }}/bin
RUN curl -L {{ index .Variables "srcDocker" }} | tar --strip-components=1 -xz \
&& rm docker
ebtables:
template: |
WORKDIR {{ index .Variables "dest" }}/{{ .Docker.CurrentStage }}
@ -394,9 +399,6 @@ tasks:
FROM alpine:3.7 AS {{ .Docker.CurrentStage }}
COPY --from={{ .Repository }}:build-phase-2 {{ index .Variables "rootfs" }} {{ index .Variables "rootfs" }}
RUN apk --update add bash
COPY src/cleanup.sh /bin/cleanup.sh
RUN chmod +x /bin/cleanup.sh
RUN /bin/cleanup.sh
FROM scratch
LABEL maintainer="Andrew Rynhard <andrew.rynhard@autonomy.io>"
COPY --from={{ .Docker.CurrentStage }} {{ index .Variables "rootfs" }} {{ index .Variables "rootfs" }}

View File

@ -9,6 +9,7 @@ metadata:
srcBzip2: http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
srcCheck: https://github.com/libcheck/check/releases/download/0.12.0/check-0.12.0.tar.gz
srcCoreutils: http://ftp.gnu.org/gnu/coreutils/coreutils-8.27.tar.xz
srcCPIO: https://ftp.gnu.org/gnu/cpio/cpio-2.12.tar.gz
srcCurl: https://curl.haxx.se/download/curl-7.56.1.tar.xz
srcDejagnu: http://ftp.gnu.org/gnu/dejagnu/dejagnu-1.6.1.tar.gz
srcDiffutils: http://ftp.gnu.org/gnu/diffutils/diffutils-3.6.tar.xz
@ -26,6 +27,7 @@ metadata:
srcGit: https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.16.2.tar.xz
srcGlibc: http://ftp.gnu.org/gnu/glibc/glibc-2.26.tar.xz
srcGmp: http://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.xz
srcGo: https://dl.google.com/go/go1.10.linux-amd64.tar.gz
srcGperf: http://ftp.gnu.org/gnu/gperf/gperf-3.0.4.tar.gz
srcGrep: http://ftp.gnu.org/gnu/grep/grep-3.1.tar.xz
srcGzip: http://ftp.gnu.org/gnu/gzip/gzip-1.8.tar.xz
@ -145,6 +147,8 @@ stages:
- kmod
- autoconf
- git
- cpio
- golang
tasks:
autoconf:
template: |
@ -258,6 +262,15 @@ tasks:
--enable-install-program=hostname
RUN make -j $(($(nproc) / 2))
RUN make install
cpio:
template: |
WORKDIR $SRC/{{ .Docker.CurrentStage }}
RUN curl -L {{ index .Variables "srcCPIO" }} | tar --strip-components=1 -xz
WORKDIR build
RUN ../configure \
--prefix=$PREFIX
RUN make
RUN make install
curl:
template: |
RUN mkdir -p /tools/etc/ssl/certs
@ -493,6 +506,11 @@ tasks:
RUN $TARGET-gcc dummy.c
RUN readelf -l a.out | grep "$PREFIX"
RUN rm -v dummy.c a.out
golang:
template: |
WORKDIR $SRC/{{ .Docker.CurrentStage }}
WORKDIR /tools/usr/local
RUN curl -L {{ index .Variables "srcGo" }} | tar -C /tools/usr/local -xz
gmp:
template: |
WORKDIR $SRC/{{ .Docker.CurrentStage }}
@ -808,7 +826,11 @@ tasks:
RUN [ "/tools/bin/bash", "-c", "rm -rf /tools/share/info /tools/share/man /tools/share/doc" ]
FROM scratch
COPY --from={{ .Docker.CurrentStage }} /tools /tools
ENV GOROOT /tools/usr/local/go
ENV GOPATH /tools/go
ENV PATH /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/tools/bin
ENV PATH $PATH:$GOROOT/bin:$GOPATH/bin
RUN [ "mkdir", "/tools/go" ]
RUN [ "mkdir", "/bin" ]
RUN [ "ln", "-s", "/tools/bin/bash", "/bin/sh" ]
RUN [ "ln", "-s", "/tools/bin/bash", "/bin/bash" ]