Initial Commit

This commit is contained in:
Darren Shepherd 2019-01-01 01:23:01 -07:00
commit 9bb7c27c62
67 changed files with 4763 additions and 0 deletions

10
.dockerignore Normal file
View File

@ -0,0 +1,10 @@
./bin
./.vagrant
./.dapper
./data-dir
./dist
./.trash-cache
./image/root
./image/agent
./image/go_build_agent
./image/main.squashfs

55
.drone.yml Normal file
View File

@ -0,0 +1,55 @@
---
pipeline:
build:
privileged: true
image: rancher/dapper:1.11.2
volumes:
- /var/run/docker.sock:/var/run/docker.sock
commands:
- dapper ci
stage-binaries:
image: rancher/dapper:1.11.2
commands:
- cp -f ./bin/rio-incluster ./package/rio
when:
branch: master
event: tag
publish-image:
image: plugins/docker
dockerfile: package/Dockerfile
repo: rancher/rio
context: package/
tag: ${DRONE_TAG}
secrets: [docker_username, docker_password]
when:
branch: master
event: tag
github_binary_prerelease:
image: plugins/github-release
prerelease: true
files:
- dist/artifacts/*
checksum:
- sha256
secrets: [github_token]
when:
branch: master
event: tag
ref:
include: [ refs/tags/*rc* ]
github_binary_release:
image: plugins/github-release
files:
- dist/artifacts/*
checksum:
- sha256
secrets: [github_token]
when:
branch: master
event: tag
ref:
exclude: [ refs/tags/*rc* ]

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
*.swp
/.dapper
/.idea
/.trash-cache
/.vagrant
/*.log
/bin
/build
/data-dir
/dist
/image/root
/image/agent
/image/go_build_agent
/image/main.squashfs
/package/rio
__pycache__
/tests/.pytest_cache/
/tests/.tox/
/tests/.vscode

11
.gometalinter.json Normal file
View File

@ -0,0 +1,11 @@
{
"EnableAll": false,
"Enable": [
"golint",
"goimports",
"misspell",
"ineffassign",
"errcheck"
],
"Deadline": "1m"
}

29
Dockerfile.dapper Normal file
View File

@ -0,0 +1,29 @@
FROM golang:1.11-alpine3.8
RUN apk -U --no-cache add bash git gcc musl-dev docker vim less file curl wget ca-certificates jq linux-headers zlib-dev tar zip squashfs-tools npm coreutils \
python3 py3-pip python3-dev openssl-dev libffi-dev
RUN pip3 install 'tox==3.6.0'
RUN npm install -g 'bats@1.1.0'
RUN apk -U --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/main/ add sqlite-dev sqlite-static
RUN go get -d golang.org/x/lint/golint && \
git -C /go/src/golang.org/x/lint/golint checkout -b current 06c8688daad7faa9da5a0c2f163a3d14aac986ca && \
go install golang.org/x/lint/golint && \
rm -rf /go/src /go/pkg
RUN go get -d github.com/alecthomas/gometalinter && \
git -C /go/src/github.com/alecthomas/gometalinter checkout -b current v2.0.11 && \
go install github.com/alecthomas/gometalinter && \
gometalinter --install && \
rm -rf /go/src /go/pkg
ENV DAPPER_RUN_ARGS --privileged
ENV DAPPER_ENV REPO TAG DRONE_TAG
ENV DAPPER_SOURCE /go/src/github.com/rancher/rio/
ENV DAPPER_OUTPUT ./bin ./dist
ENV DAPPER_DOCKER_SOCKET true
ENV HOME ${DAPPER_SOURCE}
ENV CROSS true
WORKDIR ${DAPPER_SOURCE}
ENTRYPOINT ["./scripts/entry"]
CMD ["ci"]

177
LICENSE Normal file
View File

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

23
Makefile Normal file
View File

@ -0,0 +1,23 @@
TARGETS := $(shell ls scripts)
.dapper:
@echo Downloading dapper
@curl -sL https://releases.rancher.com/dapper/latest/dapper-`uname -s`-`uname -m` > .dapper.tmp
@@chmod +x .dapper.tmp
@./.dapper.tmp -v
@mv .dapper.tmp .dapper
$(TARGETS): .dapper
./.dapper $@
trash: .dapper
./.dapper -m bind trash
trash-keep: .dapper
./.dapper -m bind trash -k
deps: trash
.DEFAULT_GOAL := ci
.PHONY: $(TARGETS)

223
agent/config/config.go Normal file
View File

@ -0,0 +1,223 @@
package config
import (
"crypto/md5"
"crypto/tls"
"encoding/hex"
"encoding/pem"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
"github.com/rancher/norman/pkg/clientaccess"
"github.com/rancher/rio/pkg/daemons/config"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/json"
net2 "k8s.io/apimachinery/pkg/util/net"
"k8s.io/client-go/util/cert"
)
type envInfo struct {
ServerURL string
Token string
DataDir string
NodeIP string
NodeName string
}
func Get() *config.Node {
for {
agentConfig, err := get()
if err != nil {
logrus.Error(err)
time.Sleep(5 * time.Second)
continue
}
return agentConfig
}
}
func getEnvInfo() (*envInfo, error) {
u := os.Getenv("K3S_URL")
if u == "" {
return nil, fmt.Errorf("K3S_URL env var is required")
}
_, err := url.Parse(u)
if err != nil {
return nil, fmt.Errorf("K3S_URL [%s] is invalid: %v", u, err)
}
t := os.Getenv("K3S_TOKEN")
if t == "" {
return nil, fmt.Errorf("K3S_TOKEN env var is required")
}
dataDir := os.Getenv("K3S_DATA_DIR")
if dataDir == "" {
return nil, fmt.Errorf("K3S_DATA_DIR is required")
}
return &envInfo{
ServerURL: u,
Token: t,
DataDir: dataDir,
NodeIP: os.Getenv("K3S_NODE_IP"),
NodeName: os.Getenv("NODE_NAME"),
}, nil
}
func getNodeCert(info *clientaccess.Info) (*tls.Certificate, error) {
nodeCert, err := clientaccess.Get("/v1-k3s/node.cert", info)
if err != nil {
return nil, err
}
nodeKey, err := clientaccess.Get("/v1-k3s/node.key", info)
if err != nil {
return nil, err
}
cert, err := tls.X509KeyPair(nodeCert, nodeKey)
if err != nil {
return nil, err
}
return &cert, nil
}
func writeNodeCA(dataDir string, nodeCert *tls.Certificate) (string, error) {
clientCABytes := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: nodeCert.Certificate[1],
})
clientCA := filepath.Join(dataDir, "client-ca.pem")
if err := ioutil.WriteFile(clientCA, clientCABytes, 0600); err != nil {
return "", errors.Wrapf(err, "failed to write client CA")
}
return clientCA, nil
}
func getHostnameAndIP(info envInfo) (string, string, error) {
ip := info.NodeIP
if ip == "" {
hostIP, err := net2.ChooseHostInterface()
if err != nil {
return "", "", err
}
ip = hostIP.String()
}
name := info.NodeName
if name == "" {
hostname, err := os.Hostname()
if err != nil {
return "", "", err
}
hostname = strings.Split(hostname, ".")[0]
d := md5.Sum([]byte(ip))
name = hostname + "-" + hex.EncodeToString(d[:])[:8]
}
return name, ip, nil
}
func localAddress(controlConfig *config.Control) string {
return fmt.Sprintf("127.0.0.1:%d", controlConfig.AdvertisePort)
}
func writeKubeConfig(envInfo *envInfo, info clientaccess.Info, controlConfig *config.Control, nodeCert *tls.Certificate) (string, error) {
os.MkdirAll(envInfo.DataDir, 0700)
kubeConfigPath := filepath.Join(envInfo.DataDir, "kubeconfig.yaml")
info.URL = "https://" + localAddress(controlConfig)
info.CACerts = pem.EncodeToMemory(&pem.Block{
Type: cert.CertificateBlockType,
Bytes: nodeCert.Certificate[1],
})
return kubeConfigPath, info.WriteKubeConfig(kubeConfigPath)
}
func get() (*config.Node, error) {
envInfo, err := getEnvInfo()
if err != nil {
return nil, err
}
serverURLParsed, err := url.Parse(envInfo.ServerURL)
if err != nil {
return nil, err
}
info, err := clientaccess.ParseAndValidateToken(envInfo.ServerURL, envInfo.Token)
if err != nil {
return nil, err
}
controlConfig, err := getConfig(info)
if err != nil {
return nil, err
}
nodeCert, err := getNodeCert(info)
if err != nil {
return nil, err
}
clientCA, err := writeNodeCA(envInfo.DataDir, nodeCert)
if err != nil {
return nil, err
}
nodeName, nodeIP, err := getHostnameAndIP(*envInfo)
if err != nil {
return nil, err
}
kubeConfig, err := writeKubeConfig(envInfo, *info, controlConfig, nodeCert)
if err != nil {
return nil, err
}
nodeConfig := &controlConfig.NodeConfig
nodeConfig.LocalAddress = localAddress(controlConfig)
nodeConfig.AgentConfig.NodeIP = defString(nodeConfig.AgentConfig.NodeIP, nodeIP)
nodeConfig.AgentConfig.NodeName = defString(nodeConfig.AgentConfig.NodeName, nodeName)
nodeConfig.AgentConfig.CNIBinDir = defString(nodeConfig.AgentConfig.CNIBinDir, "/usr/share/cni")
nodeConfig.AgentConfig.CACertPath = clientCA
nodeConfig.AgentConfig.ListenAddress = defString(nodeConfig.AgentConfig.ListenAddress, "127.0.0.1")
nodeConfig.AgentConfig.KubeConfig = kubeConfig
nodeConfig.CACerts = info.CACerts
nodeConfig.ServerAddress = serverURLParsed.Host
nodeConfig.Certificate = nodeCert
if !nodeConfig.Docker {
nodeConfig.AgentConfig.RuntimeSocket = "/run/k3s/containerd.sock"
}
return nodeConfig, nil
}
func defString(val, newVal string) string {
if val == "" {
return newVal
}
return val
}
func getConfig(info *clientaccess.Info) (*config.Control, error) {
data, err := clientaccess.Get("/v1-k3s/config", info)
if err != nil {
return nil, err
}
controlControl := &config.Control{}
return controlControl, json.Unmarshal(data, controlControl)
}

View File

@ -0,0 +1,89 @@
package containerd
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"syscall"
"time"
"github.com/rancher/rio/agent/config"
util2 "github.com/rancher/rio/agent/util"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
"k8s.io/kubernetes/pkg/kubelet/util"
)
const (
address = "/run/k3s/containerd.sock"
maxMsgSize = 1024 * 1024 * 16
configToml = `[plugins.cri]
stream_server_address = "%NODE%"
stream_server_port = "10010"
[plugins.cri.cni]
bin_dir = "/usr/share/cni/bin"
conf_dir = "/etc/cni/net.d"
`
)
func Run(ctx context.Context, config *config.NodeConfig) error {
args := []string{
"containerd",
"-a", address,
"--state", "/run/k3s/containerd",
}
if err := util2.WriteFile("/etc/containerd/config.toml",
strings.Replace(configToml, "%NODE%", config.AgentConfig.NodeName, -1)); err != nil {
return err
}
if logrus.GetLevel() >= logrus.DebugLevel {
args = append(args, "--verbose")
}
go func() {
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGKILL,
}
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "containerd: %s\n", err)
}
os.Exit(1)
}()
for {
addr, dailer, err := util.GetAddressAndDialer("unix://" + address)
if err != nil {
time.Sleep(1 * time.Second)
continue
}
conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(3*time.Second), grpc.WithDialer(dailer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
if err != nil {
time.Sleep(1 * time.Second)
continue
}
c := runtimeapi.NewRuntimeServiceClient(conn)
_, err = c.Version(ctx, &runtimeapi.VersionRequest{
Version: "0.1.0",
})
if err == nil {
conn.Close()
break
}
conn.Close()
logrus.Infof("Waiting for containerd startup")
time.Sleep(1 * time.Second)
}
return nil
}

140
agent/flannel/flannel.go Normal file
View File

@ -0,0 +1,140 @@
// Copyright 2015 flannel authors
//
// 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 flannel
import (
"fmt"
"net"
"os"
"path/filepath"
"sync"
"github.com/coreos/flannel/backend"
"github.com/coreos/flannel/network"
"github.com/coreos/flannel/pkg/ip"
"github.com/coreos/flannel/subnet/kube"
"golang.org/x/net/context"
log "k8s.io/klog"
// Backends need to be imported for their init() to get executed and them to register
_ "github.com/coreos/flannel/backend/vxlan"
)
const (
subnetFile = "/run/flannel/subnet.env"
)
func flannel(ctx context.Context, kubeConfigFile string) error {
extIface, err := LookupExtIface()
if err != nil {
return err
}
sm, err := kube.NewSubnetManager("", kubeConfigFile)
if err != nil {
return err
}
config, err := sm.GetNetworkConfig(ctx)
if err != nil {
return err
}
// Create a backend manager then use it to create the backend and register the network with it.
bm := backend.NewManager(ctx, sm, extIface)
be, err := bm.GetBackend(config.BackendType)
if err != nil {
return err
}
bn, err := be.RegisterNetwork(ctx, sync.WaitGroup{}, config)
if err != nil {
return err
}
go network.SetupAndEnsureIPTables(network.MasqRules(config.Network, bn.Lease()), 60)
go network.SetupAndEnsureIPTables(network.ForwardRules(config.Network.String()), 50)
if err := WriteSubnetFile(subnetFile, config.Network, true, bn); err != nil {
// Continue, even though it failed.
log.Warningf("Failed to write subnet file: %s", err)
} else {
log.Infof("Wrote subnet file to %s", subnetFile)
}
// Start "Running" the backend network. This will block until the context is done so run in another goroutine.
log.Info("Running backend.")
bn.Run(ctx)
return nil
}
func LookupExtIface() (*backend.ExternalInterface, error) {
var iface *net.Interface
var ifaceAddr net.IP
var err error
log.Info("Determining IP address of default interface")
if iface, err = ip.GetDefaultGatewayIface(); err != nil {
return nil, fmt.Errorf("failed to get default interface: %s", err)
}
ifaceAddr, err = ip.GetIfaceIP4Addr(iface)
if err != nil {
return nil, fmt.Errorf("failed to find IPv4 address for interface %s", iface.Name)
}
log.Infof("Using interface with name %s and address %s", iface.Name, ifaceAddr)
if iface.MTU == 0 {
return nil, fmt.Errorf("failed to determine MTU for %s interface", ifaceAddr)
}
return &backend.ExternalInterface{
Iface: iface,
IfaceAddr: ifaceAddr,
ExtAddr: ifaceAddr,
}, nil
}
func WriteSubnetFile(path string, nw ip.IP4Net, ipMasq bool, bn backend.Network) error {
dir, name := filepath.Split(path)
os.MkdirAll(dir, 0755)
tempFile := filepath.Join(dir, "."+name)
f, err := os.Create(tempFile)
if err != nil {
return err
}
// Write out the first usable IP by incrementing
// sn.IP by one
sn := bn.Lease().Subnet
sn.IP += 1
fmt.Fprintf(f, "FLANNEL_NETWORK=%s\n", nw)
fmt.Fprintf(f, "FLANNEL_SUBNET=%s\n", sn)
fmt.Fprintf(f, "FLANNEL_MTU=%d\n", bn.MTU())
_, err = fmt.Fprintf(f, "FLANNEL_IPMASQ=%v\n", ipMasq)
f.Close()
if err != nil {
return err
}
// rename(2) the temporary file to the desired location so that it becomes
// atomically visible with the contents
return os.Rename(tempFile, path)
//TODO - is this safe? What if it's not on the same FS?
}

91
agent/flannel/setup.go Normal file
View File

@ -0,0 +1,91 @@
package flannel
import (
"context"
"strings"
"time"
"github.com/rancher/rio/agent/util"
"github.com/rancher/rio/agent/config"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
const (
cniConf = `{
"name":"cbr0",
"cniVersion":"0.3.1",
"plugins":[
{
"type":"flannel",
"delegate":{
"forceAddress":true,
"isDefaultGateway":true
}
},
{
"type":"portmap",
"capabilities":{
"portMappings":true
}
}
]
}
`
netJson = `{
"Network": "%CIDR%",
"Backend": {
"Type": "vxlan"
}
}
`
)
func Run(ctx context.Context, config *config.NodeConfig) error {
nodeName := config.AgentConfig.NodeName
restConfig, err := clientcmd.BuildConfigFromFlags("", config.AgentConfig.KubeConfig)
if err != nil {
return err
}
client, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return err
}
for {
node, err := client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
if err == nil && node.Spec.PodCIDR != "" {
break
}
if err == nil {
logrus.Infof("waiting for node %s CIDR not assigned yet", nodeName)
} else {
logrus.Infof("waiting for node %s: %v", nodeName, err)
}
time.Sleep(2 * time.Second)
}
if err := createCNIConf(); err != nil {
return err
}
if err := createFlannelConf(config); err != nil {
return err
}
return flannel(ctx, config.AgentConfig.KubeConfig)
}
func createCNIConf() error {
return util.WriteFile("/etc/cni/net.d/10-flannel.conflist", cniConf)
}
func createFlannelConf(config *config.NodeConfig) error {
return util.WriteFile("/etc/kube-flannel/net-conf.json",
strings.Replace(netJson, "%CIDR", config.AgentConfig.ClusterCIDR.String(), -1))
}

58
agent/main.go Normal file
View File

@ -0,0 +1,58 @@
package main
import (
"context"
"github.com/rancher/norman/signal"
"github.com/rancher/rio/agent/config"
"github.com/rancher/rio/agent/containerd"
"github.com/rancher/rio/agent/flannel"
"github.com/rancher/rio/agent/proxy"
"github.com/rancher/rio/agent/syssetup"
"github.com/rancher/rio/agent/tunnel"
"github.com/rancher/rio/pkg/daemons/agent"
"github.com/sirupsen/logrus"
)
func main() {
if err := run(); err != nil {
logrus.Fatal(err)
}
}
func run() error {
ctx := signal.SigTermCancelContext(context.Background())
nodeConfig := config.Get()
if nodeConfig.Docker {
nodeConfig.AgentConfig.RuntimeSocket = ""
} else {
containerd.Run(ctx, nodeConfig)
}
if err := syssetup.Configure(); err != nil {
return err
}
if err := tunnel.Setup(nodeConfig); err != nil {
return err
}
if err := proxy.Run(nodeConfig); err != nil {
return err
}
if err := agent.Agent(&nodeConfig.AgentConfig); err != nil {
return err
}
if !nodeConfig.NoFlannel {
if err := flannel.Run(ctx, nodeConfig); err != nil {
return err
}
}
<-ctx.Done()
return ctx.Err()
}

35
agent/proxy/proxy.go Normal file
View File

@ -0,0 +1,35 @@
package proxy
import (
"crypto/tls"
"net/http"
"github.com/pkg/errors"
"github.com/rancher/norman/pkg/proxy"
"github.com/rancher/rio/agent/config"
"github.com/sirupsen/logrus"
)
func Run(config *config.NodeConfig) error {
proxy, err := proxy.NewSimpleProxy(config.ServerAddress, config.CACerts, true)
if err != nil {
return err
}
listener, err := tls.Listen("tcp", config.LocalAddress, &tls.Config{
Certificates: []tls.Certificate{
*config.Certificate,
},
})
if err != nil {
return errors.Wrap(err, "Failed to start tls listener")
}
go func() {
err := http.Serve(listener, proxy)
logrus.Fatalf("TLS proxy stopped: %v", err)
}()
return nil
}

18
agent/syssetup/setup.go Normal file
View File

@ -0,0 +1,18 @@
package syssetup
import (
"io/ioutil"
"github.com/sirupsen/logrus"
)
var (
callIPTablesFile = "/proc/sys/net/bridge/bridge-nf-call-iptables"
)
func Configure() error {
if err := ioutil.WriteFile(callIPTablesFile, []byte("1"), 0640); err != nil {
logrus.Warnf("failed to write value 1 at %s: %v", callIPTablesFile, err)
}
return nil
}

79
agent/tunnel/tunnel.go Normal file
View File

@ -0,0 +1,79 @@
package tunnel
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"net"
"net/http"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/rancher/norman/pkg/remotedialer"
"github.com/rancher/rio/agent/config"
"github.com/sirupsen/logrus"
"k8s.io/client-go/tools/clientcmd"
)
var (
ports = map[string]bool{
"10250": true,
"10010": true,
}
)
func Setup(config *config.NodeConfig) error {
restConfig, err := clientcmd.BuildConfigFromFlags("", config.AgentConfig.KubeConfig)
if err != nil {
return err
}
transportConfig, err := restConfig.TransportConfig()
if err != nil {
return err
}
wsURL := fmt.Sprintf("wss://%s/v1/connect", config.ServerAddress)
headers := map[string][]string{
"X-K3s-NodeName": {config.AgentConfig.NodeName},
}
ws := &websocket.Dialer{}
if len(config.CACerts) > 0 {
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(config.CACerts)
ws.TLSClientConfig = &tls.Config{
RootCAs: pool,
}
}
if transportConfig.Username != "" {
auth := transportConfig.Username + ":" + transportConfig.Password
auth = base64.StdEncoding.EncodeToString([]byte(auth))
headers["Authorization"] = []string{"Basic " + auth}
}
once := sync.Once{}
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
for {
logrus.Infof("Connecting to %s", wsURL)
remotedialer.ClientConnect(wsURL, http.Header(headers), ws, func(proto, address string) bool {
host, port, err := net.SplitHostPort(address)
return err == nil && proto == "tcp" && ports[port] && host == "127.0.0.1"
}, func(_ context.Context) error {
once.Do(wg.Done)
return nil
})
time.Sleep(5 * time.Second)
}
}()
wg.Wait()
return nil
}

18
agent/util/file.go Normal file
View File

@ -0,0 +1,18 @@
package util
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
)
func WriteFile(name string, content string) error {
os.Mkdir(filepath.Dir(name), 0755)
err := ioutil.WriteFile(name, []byte(content), 0644)
if err != nil {
return errors.Wrapf(err, "writing %s", name)
}
return nil
}

19
cli/cmd/agent/agent.go Normal file
View File

@ -0,0 +1,19 @@
package agent
import "github.com/urfave/cli"
type Agent struct {
T_Token string `desc:"Token to use for authentication" env:"K3S_TOKEN"`
S_Server string `desc:"Server to connect to" env:"K3S_URL"`
D_DataDir string `desc:"Folder to hold state" default:"/var/lib/rancher/k3s"`
L_Log string `desc:"log to file"`
AgentShared
}
type AgentShared struct {
I_NodeIp string `desc:"IP address to advertise for node"`
}
func (a *Agent) Customize(command *cli.Command) {
command.Category = "CLUSTER RUNTIME"
}

View File

@ -0,0 +1,81 @@
// +build k8s
package agent
import (
"fmt"
"io"
"os"
"path/filepath"
"time"
"github.com/natefinch/lumberjack"
"github.com/rancher/norman/pkg/clientaccess"
"github.com/rancher/norman/pkg/resolvehome"
"github.com/rancher/rio/pkg/enterchroot"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func (a *Agent) Run(ctx *cli.Context) error {
if os.Getuid() != 0 {
return fmt.Errorf("agent must be ran as root")
}
if len(a.T_Token) == 0 {
return fmt.Errorf("--token is required")
}
if len(a.S_Server) == 0 {
return fmt.Errorf("--server is required")
}
dataDir, err := resolvehome.Resolve(a.D_DataDir)
if err != nil {
return err
}
return RunAgent(a.S_Server, a.T_Token, dataDir, a.L_Log, a.I_NodeIp)
}
func RunAgent(server, token, dataDir, logFile, ipAddress string) error {
dataDir = filepath.Join(dataDir, "agent")
for {
tmpFile, err := clientaccess.AgentAccessInfoToTempKubeConfig("", server, token)
if err != nil {
logrus.Error(err)
time.Sleep(2 * time.Second)
continue
}
os.Remove(tmpFile)
break
}
os.Setenv("K3S_URL", server)
os.Setenv("K3S_TOKEN", token)
os.Setenv("K3S_DATA_DIR", dataDir)
os.Setenv("K3S_NODE_IP", ipAddress)
os.MkdirAll(dataDir, 0700)
stdout := io.Writer(os.Stdout)
stderr := io.Writer(os.Stderr)
if logFile == "" {
stdout = os.Stdout
stderr = os.Stderr
} else {
l := &lumberjack.Logger{
Filename: logFile,
MaxSize: 50,
MaxBackups: 3,
MaxAge: 28,
Compress: true,
}
stdout = l
stderr = l
}
return enterchroot.Mount(filepath.Join(dataDir, "root"), stdout, stderr, os.Args[1:])
}

View File

@ -0,0 +1,17 @@
// +build !k8s
package agent
import (
"fmt"
"github.com/urfave/cli"
)
func (a *Agent) Run(ctx *cli.Context) error {
return fmt.Errorf("agent support is not compiled in, add \"-tags k8s\" to \"go build\"")
}
func RunAgent(server, token, dataDir, logFile string) error {
return fmt.Errorf("agent support is not compiled in, add \"-tags k8s\" to \"go build\"")
}

View File

@ -0,0 +1,24 @@
package kubectl
import (
"os"
"github.com/rancher/rio/pkg/kubectl"
"github.com/urfave/cli"
)
func NewKubectlCommand() cli.Command {
return cli.Command{
Name: "kubectl",
Usage: "Run kubectl",
SkipFlagParsing: true,
SkipArgReorder: true,
Action: run,
}
}
func run(ctx *cli.Context) error {
os.Args = append([]string{"kubectl"}, ctx.Args()...)
kubectl.Main()
return nil
}

128
cli/cmd/server/server.go Normal file
View File

@ -0,0 +1,128 @@
package server
import (
"context"
"flag"
"fmt"
"os"
"path/filepath"
"github.com/docker/docker/pkg/reexec"
"github.com/natefinch/lumberjack"
"github.com/rancher/norman/signal"
"github.com/rancher/rio/pkg/server"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"k8s.io/apimachinery/pkg/util/net"
)
var (
appName = filepath.Base(os.Args[0])
config server.Config
log string
)
var ServerCommand = cli.Command{
Name: "server",
Usage: "Run management server",
UsageText: appName + " server [OPTIONS]",
Action: Run,
Flags: []cli.Flag{
cli.IntFlag{
Name: "https-listen-port",
Usage: "HTTPS listen port",
Value: 6443,
Destination: &config.TLSConfig.HTTPSPort,
},
cli.IntFlag{
Name: "http-listen-port",
Usage: "HTTP listen port (for /healthz, HTTPS redirect, and port for TLS terminating LB)",
Value: 0,
Destination: &config.TLSConfig.HTTPPort,
},
cli.StringFlag{
Name: "data-dir",
Usage: "Folder to hold state default /var/lib/rancher/k3s or ${HOME}/.rancher/k3s if not root",
Destination: &config.ControlConfig.DataDir,
},
//cli.StringFlag{
// Name: "advertise-address",
// Usage: "Address of the server to put in the generated kubeconfig",
// Destination: &config.AdvertiseIP,
//},
cli.BoolFlag{
Name: "disable-agent",
Usage: "Do not run a local agent and register a local kubelet",
Destination: &config.DisableAgent,
},
cli.StringFlag{
Name: "log",
Usage: "Log to file",
Destination: &log,
},
},
}
func setupLogging(app *cli.Context) {
if !app.GlobalBool("debug") {
flag.Set("stderrthreshold", "3")
flag.Set("alsologtostderr", "false")
flag.Set("logtostderr", "false")
}
}
func runWithLogging(app *cli.Context) error {
l := &lumberjack.Logger{
Filename: log,
MaxSize: 50,
MaxBackups: 3,
MaxAge: 28,
Compress: true,
}
args := append([]string{"k3s"}, os.Args[1:]...)
cmd := reexec.Command(args...)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "_RIO_REEXEC_=true")
cmd.Stderr = l
cmd.Stdout = l
cmd.Stdin = os.Stdin
return cmd.Run()
}
func Run(app *cli.Context) error {
if log != "" && os.Getenv("_RIO_REEXEC_") == "" {
return runWithLogging(app)
}
setupLogging(app)
if !config.DisableAgent && os.Getuid() != 0 {
return fmt.Errorf("must run as root unless --disable-agent is specified")
}
if config.ControlConfig.NodeConfig.AgentConfig.NodeIP == "" {
ip, err := net.ChooseHostInterface()
if err == nil {
config.ControlConfig.NodeConfig.AgentConfig.NodeIP = ip.String()
}
}
logrus.Info("Starting k3s ", app.App.Version)
ctx := signal.SigTermCancelContext(context.Background())
if err := server.StartServer(ctx, &config); err != nil {
return err
}
if config.DisableAgent {
<-ctx.Done()
return nil
}
return nil
//logFile := filepath.Join(serverConfig.DataDir, "agent/agent.log")
//url := fmt.Sprintf("https://localhost:%d", httpsListenPort)
//logrus.Infof("Agent starting, logging to %s", logFile)
//return agent.RunAgent(url, server2.FormatToken(serverConfig.Runtime.NodeToken), serverConfig.DataDir, logFile, "")
}

151
cli/pkg/builder/builder.go Normal file
View File

@ -0,0 +1,151 @@
package builder
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"unsafe"
"github.com/urfave/cli"
)
var (
caseRegexp = regexp.MustCompile("([a-z])([A-Z])")
)
type clirunnable interface {
Run(app *cli.Context) error
}
type customizer interface {
Customize(cmd *cli.Command)
}
type fieldInfo struct {
FieldType reflect.StructField
FieldValue reflect.Value
}
func fields(obj interface{}) []fieldInfo {
ptrValue := reflect.ValueOf(obj)
objValue := ptrValue.Elem()
var result []fieldInfo
for i := 0; i < objValue.NumField(); i++ {
fieldType := objValue.Type().Field(i)
if fieldType.Anonymous && fieldType.Type.Kind() == reflect.Struct {
result = append(result, fields(objValue.Field(i).Addr().Interface())...)
} else if !fieldType.Anonymous {
result = append(result, fieldInfo{
FieldValue: objValue.Field(i),
FieldType: objValue.Type().Field(i),
})
}
}
return result
}
func Command(obj interface{}, usage, usageText, description string) cli.Command {
slices := map[string]reflect.Value{}
maps := map[string]reflect.Value{}
ptrValue := reflect.ValueOf(obj)
objValue := ptrValue.Elem()
c := cli.Command{
Name: strings.ToLower(strings.Replace(objValue.Type().Name(), "Command", "", 1)),
Usage: usage,
UsageText: usageText,
Description: description,
UseShortOptionHandling: true,
SkipArgReorder: true,
}
for _, info := range fields(obj) {
defMessage := ""
fieldType := info.FieldType
v := info.FieldValue
switch fieldType.Type.Kind() {
case reflect.Int:
flag := cli.IntFlag{
Name: name(fieldType.Name),
Usage: fieldType.Tag.Get("desc"),
EnvVar: fieldType.Tag.Get("env"),
Destination: (*int)(unsafe.Pointer(v.Addr().Pointer())),
}
defValue := fieldType.Tag.Get("default")
if defValue != "" {
n, err := strconv.Atoi(defValue)
if err != nil {
panic("bad default " + defValue + " on field " + fieldType.Name)
}
flag.Value = n
}
c.Flags = append(c.Flags, flag)
case reflect.String:
flag := cli.StringFlag{
Name: name(fieldType.Name),
Usage: fieldType.Tag.Get("desc"),
Value: fieldType.Tag.Get("default"),
EnvVar: fieldType.Tag.Get("env"),
Destination: (*string)(unsafe.Pointer(v.Addr().Pointer())),
}
c.Flags = append(c.Flags, flag)
case reflect.Slice:
slices[name(fieldType.Name)] = v
defMessage = " "
fallthrough
case reflect.Map:
if defMessage == "" {
maps[name(fieldType.Name)] = v
defMessage = " "
}
flag := cli.StringSliceFlag{
Name: name(fieldType.Name),
Usage: fieldType.Tag.Get("desc") + defMessage,
EnvVar: fieldType.Tag.Get("env"),
Value: &cli.StringSlice{},
}
c.Flags = append(c.Flags, flag)
case reflect.Bool:
flag := cli.BoolFlag{
Name: name(fieldType.Name),
Usage: fieldType.Tag.Get("desc"),
EnvVar: fieldType.Tag.Get("env"),
Destination: (*bool)(unsafe.Pointer(v.Addr().Pointer())),
}
c.Flags = append(c.Flags, flag)
default:
panic("Unknown kind on field " + fieldType.Name + " on " + objValue.Type().Name())
}
}
if run, ok := obj.(clirunnable); ok {
c.Action = run.Run
} else {
panic(fmt.Sprintf("failed to find Action function for %T", obj))
}
cust, ok := obj.(customizer)
if ok {
cust.Customize(&c)
}
return c
}
func name(name string) string {
parts := strings.Split(name, "_")
i := len(parts) - 1
name = caseRegexp.ReplaceAllString(parts[i], "$1-$2")
name = strings.ToLower(name)
result := append([]string{name}, parts[0:i]...)
for i := 0; i < len(result); i++ {
result[i] = strings.ToLower(result[i])
}
return strings.Join(result, ",")
}

179
image/Dockerfile Normal file
View File

@ -0,0 +1,179 @@
### BASE ###
FROM alpine:3.8 as base
RUN apk -U add findutils iptables ipset bash ca-certificates jq iproute2 nfs-utils coreutils libseccomp conntrack-tools
# RUN apk -U add eudev tinyssh e2fsprogs mdadm rsync nfs-utils parted
FROM base as k3s-build
COPY --from=base /bin /usr/src/image/bin/
COPY --from=base /lib /usr/src/image/lib/
COPY --from=base /sbin /usr/src/image/sbin/
COPY --from=base /etc/ssl/certs/ca-certificates.crt /usr/src/image/etc/ssl/certs/ca-certificates.crt
COPY --from=base /etc/terminfo /usr/src/image/etc/terminfo
COPY --from=base /usr /usr/src/image/usr/
WORKDIR /usr/src/image
RUN rm -rf usr/bin/iconv \
usr/bin/scanelf \
usr/bin/ssl_client \
usr/bin/pkgconf \
usr/bin/getent \
usr/bin/locate \
usr/bin/updatedb \
usr/bin/c_rehash \
usr/bin/getconf \
usr/etc \
usr/include \
usr/lib/bash \
usr/lib/krb5 \
usr/lib/pkgconfig \
usr/lib/tc \
usr/libexec \
usr/local \
usr/sbin/nfsiostat \
usr/sbin/rpc.gssd \
usr/sbin/nfsidmap \
usr/sbin/blkmapd \
usr/sbin/conntrackd \
usr/sbin/nfct \
usr/sbin/nfsstat \
usr/sbin/mountstats \
usr/sbin/setcap \
usr/sbin/exportfs \
usr/sbin/update-ca-certificates \
usr/sbin/capsh \
usr/sbin/getcap \
usr/sbin/rpcdebug \
usr/sbin/start-statd \
usr/sbin/getpcaps \
usr/sbin/sm-notify \
usr/share/aclocal \
usr/share/apk \
usr/share/ca-certificates \
usr/share/man \
usr/share/misc && \
find usr/share/terminfo -type f -exec rm {} \; && \
ln -s xterm-color usr/share/terminfo/x/xterm-256color && \
rmdir usr/share/terminfo/* || true
RUN rm -rf bin/sh \
lib/apk \
lib/mdev \
sbin/ss \
sbin/routel \
sbin/*-compat* \
sbin/genl \
sbin/lnstat \
sbin/ifstat \
sbin/mkmntdirs \
sbin/nfsdcltrack \
sbin/rtacct \
sbin/nstat \
sbin/routef \
sbin/apk \
sbin/tc \
sbin/ifcfg \
sbin/setup-udev \
sbin/rtpr \
sbin/osd_login \
sbin/bridge \
sbin/rtmon && \
ln -s bash bin/sh && \
mkdir -p lib/modules
RUN mv sbin/* bin/ && \
rmdir sbin && \
ln -s bin sbin
RUN mkdir lib2 && \
mv usr/lib/* lib2/ && \
mv lib2/* lib/ && \
mv usr/bin/* bin/ && \
mv usr/sbin/* bin/ && \
mv usr/share . && \
rm -rf usr lib2 && \
for i in $(ls -l bin | grep usr/bin/coreutils | awk '{print $(NF-2)}'); do \
rm bin/$i && ln -s coreutils bin/$i \
;done && \
find -L bin -type l -exec rm {} \; -print
RUN apk add upx && \
upx $(find bin -type f -executable \! -name coreutils) || true
RUN echo '#### LAYOUT #####' && \
find -type d && \
echo '#### BIN #####' && \
find bin -type f -executable && \
du -x -s -h
RUN tar cf ../rootfs.tar * && \
ls -la ../rootfs.tar
CMD ["sh"]
### BUILD IMAGE ###
FROM golang:1.11-alpine AS gobuild
RUN apk -U add git gcc linux-headers musl-dev make libseccomp libseccomp-dev bash
RUN rm -f /bin/sh && ln -s /bin/bash /bin/sh
### CNI ###
FROM gobuild AS cni
RUN mkdir -p $GOPATH/src/github.com/containernetworking && \
cd $GOPATH/src/github.com/containernetworking && \
git clone https://github.com/ibuildthecloud/plugins.git && \
cd plugins && \
git checkout 9810b7d5137b171c4e07ce59bb18be9feccec557
RUN go build -buildmode=pie -ldflags -s -o /usr/bin/cni github.com/containernetworking/plugins
### RUNC ###
FROM gobuild AS runc
RUN go get -d github.com/opencontainers/runc && \
git -C $GOPATH/src/github.com/opencontainers/runc checkout -b build v1.0.0-rc5
WORKDIR $GOPATH/src/github.com/opencontainers/runc
RUN make runc && \
cp runc /usr/bin/
### CONTAINERD ###
FROM gobuild AS containerd
RUN go get -d github.com/containerd/containerd && \
git -C $GOPATH/src/github.com/containerd/containerd checkout -b build v1.1.4
WORKDIR $GOPATH/src/github.com/containerd/containerd
RUN sed -i -e '/aufs/d' -e '/zfs/d' cmd/containerd/builtins_linux.go
RUN make BUILDTAGS="apparmor seccomp no_btrfs netgo osusergo" bin/containerd bin/containerd-shim && \
cp bin/containerd bin/containerd-shim /usr/bin/
### AGENT ###
FROM gobuild AS agent
ADD /build/vendor.tar $GOPATH/src/github.com/rancher/rio/
COPY /agent $GOPATH/src/github.com/rancher/rio/agent
WORKDIR $GOPATH/src/github.com/rancher/rio
RUN go build -buildmode=pie -tags k3s -ldflags -s -o /usr/bin/agent ./agent
### ASSEMBLE IMAGE ###
FROM gobuild
RUN apk add -U squashfs-tools
COPY --from=k3s-build /usr/src/rootfs.tar /usr/src/rootfs.tar
RUN mkdir /usr/src/image && \
tar xf /usr/src/rootfs.tar -C /usr/src/image
COPY --from=runc /usr/bin/runc /usr/src/image/bin/runc
RUN strip --strip-unneeded /usr/src/image/bin/runc
COPY --from=agent /usr/bin/agent /usr/src/image/bin/
RUN strip --strip-unneeded /usr/src/image/bin/agent
COPY --from=containerd /usr/bin/containerd-shim /usr/bin/containerd /usr/src/image/bin/
#COPY containerd /usr/src/image/bin
RUN strip --strip-unneeded /usr/src/image/bin/containerd /usr/src/image/bin/containerd-shim
RUN mkdir -p /usr/src/image/share/cni/bin
COPY --from=cni /usr/bin/cni /usr/src/image/share/cni/bin
RUN cd /usr/src/image/share/cni/bin && \
for i in ./bridge ./flannel ./host-local ./loopback ./portmap; do \
ln -s cni $i \
;done
COPY image/init /usr/src/image/init
RUN mksquashfs /usr/src/image main.squashfs

19
image/build Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
set -e
cd $(dirname $0)
rm -rf main.squashfs
pushd ..
mkdir -p build
echo Copying vendor
tar chf build/vendor.tar \
--exclude vendor/github.com/coreos/etcd/cmd \
--exclude vendor/github.com/jteeuwen/go-bindata/testdata \
--exclude vendor/github.com/karrick/godirwalk/testdata \
vendor
echo Running agent docker build
docker build -t bb -f image/Dockerfile .
popd
docker run --rm bb cat main.squashfs > main.squashfs

147
image/init Executable file
View File

@ -0,0 +1,147 @@
#!/usr/bin/sh
set -e
if [ "$ENTER_DEBUG" = true ]; then
set -x
fi
layout()
{
mount --make-rshared /
mkdir -p /proc
mount -t proc -o nodev,nosuid,noexec,relatime none /proc
for i in cache empty lib local local log opt spool tmp; do
mkdir -p /var/$i
done
for i in run dev home mnt media opt root lib/modules lib/firmware var/lib/docker; do
if [ -d /.root/$i ]; then
mkdir -p /$i
mount --rbind /.root/$i /$i
fi
done
if [ -L /.root/var/run ]; then
ln -sf /run /var/run
else
mkdir -p /var/run
mount --rbind /.root/var/run /var/run
fi
mkdir -p $K3S_DATA_DIR
mount --rbind /.root/$K3S_DATA_DIR /$K3S_DATA_DIR
mkdir -p /run/k3s/containerd
}
sysfs()
{
mkdir -p /sys
mount -t sysfs none /sys
mount -t securityfs -o noexec,nosuid,nodev none /sys/kernel/security 2>/dev/null|| true
mount -t configfs -o noexec,nosuid,nodev none /sys/kernel/config 2>/dev/null || true
mount -t fusectl -o noexec,nosuid,nodev none /sys/fs/fuse/connections 2>/dev/null || true
mount -t binfmt_misc -o noexec,nosuid,nodev none /proc/sys/fs/binfmt_misc 2>/dev/null || true
}
cgroups()
{
mount -t tmpfs -o mode=755,size=10m none /sys/fs/cgroup
cat /proc/cgroups > /tmp/cgroups
for i in $(seq 0 20); do
t=""
l="$(cat /tmp/cgroups | grep '1$' | awk '{print $1 " " $2}' | grep -w $i | awk '{print $1}')"
for j in $l; do
if [ -z "$t" ]; then
t=$j
else
t="${t},$j"
fi
done
if [ -z "$t" ]; then
continue
fi
mkdir -p /sys/fs/cgroup/${t}
mount -t cgroup -o ${t},noexec,nosuid,nodev none /sys/fs/cgroup/${t}
mkdir -p /sys/fs/cgroup/${t}/k3s
for j in $l; do
if [ $j != $t ]; then
ln -s $t /sys/fs/cgroup/$j
fi
done
done
# good ole systemd
mkdir -p /sys/fs/cgroup/systemd
mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd
mkdir -p /sys/fs/cgroup/systemd/k3s
rm /tmp/cgroups
}
mketc()
{
mkdir -p /etc
cp -rf usr/etc/* /etc/
for i in /.root/usr/lib/os-release /.root/etc/os-release; do
if [ -e $i ]; then
cp -f $i /etc/os-release
fi
done
if [ -e /.root/etc/machine-id ]; then
cp -f /.root/etc/machine-id /etc/machine-id
fi
hostname > /etc/hostname
cat > /etc/hosts << EOF
127.0.0.1 localhost $NODE_NAME $(hostname)
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF
cat > /etc/resolv.conf << EOF
nameserver 1.1.1.1
EOF
}
nodename()
{
if [ ! -e $K3S_DATA_DIR/id ]; then
echo $RANDOM > $K3S_DATA_DIR/id
fi
export NODE_NAME="$(hostname | cut -f1 -d.)-$(<$K3S_DATA_DIR/id)"
}
layout
nodename
mketc
sysfs
cgroups
umount -l .root
rmdir .root
if [ "$1" = "--" ]; then
shift 1
exec "$@"
fi
exec env -i -- \
HOME=/root \
NODE_NAME=$NODE_NAME \
PATH=/sbin:/bin \
K3S_DATA_DIR=$K3S_DATA_DIR \
K3S_NODE_IP=$K3S_NODE_IP \
K3S_TOKEN=$K3S_TOKEN \
K3S_URL=$K3S_URL \
agent

71
main.go Normal file
View File

@ -0,0 +1,71 @@
//go:generate go run types/codegen/cleanup/main.go
//go:generate go run types/codegen/main.go
package main
import (
"fmt"
"os"
"path/filepath"
"github.com/docker/docker/pkg/reexec"
"github.com/rancher/rio/cli/cmd/agent"
"github.com/rancher/rio/cli/cmd/kubectl"
"github.com/rancher/rio/cli/cmd/server"
"github.com/rancher/rio/cli/pkg/builder"
"github.com/rancher/rio/version"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
_ "github.com/rancher/rio/pkg/kubectl"
)
var (
appName = filepath.Base(os.Args[0])
debug bool
)
func main() {
old := os.Args[0]
os.Args[0] = filepath.Base(os.Args[0])
if reexec.Init() {
return
}
os.Args[0] = old
app := cli.NewApp()
app.Name = appName
app.Usage = "Kubernetes, but small and simple"
app.Version = version.Version
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("%s version %s\n", app.Name, app.Version)
}
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "debug",
Usage: "Turn on debug logs",
Destination: &debug,
},
}
app.Commands = []cli.Command{
server.ServerCommand,
builder.Command(&agent.Agent{},
"Run node agent",
appName+" agent [OPTIONS]",
""),
kubectl.NewKubectlCommand(),
}
app.Before = func(ctx *cli.Context) error {
if debug {
logrus.SetLevel(logrus.DebugLevel)
}
return nil
}
err := app.Run(os.Args)
if err != nil {
logrus.Fatal(err)
}
}

5
package/Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM alpine:3.8
RUN apk -U --no-cache add ca-certificates
COPY rio /
WORKDIR /var/lib/rancher/rio

View File

@ -0,0 +1,12 @@
package tls
import (
"context"
v1 "github.com/rancher/rio/types/apis/k3s.cattle.io/v1"
)
func Register(ctx context.Context) error {
clients := v1.ClientsFrom(ctx)
clients.ListenerConfig.OnChange(ctx)
}

View File

@ -0,0 +1,98 @@
package agent
import (
"context"
"math/rand"
"time"
"github.com/rancher/rio/pkg/daemons/config"
"github.com/sirupsen/logrus"
"k8s.io/apiserver/pkg/util/logs"
app2 "k8s.io/kubernetes/cmd/kube-proxy/app"
"k8s.io/kubernetes/cmd/kubelet/app"
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
)
func Agent(config *config.Agent) error {
rand.Seed(time.Now().UTC().UnixNano())
prepare(config)
kubelet(config)
kubeProxy(config)
return nil
}
func prepare(config *config.Agent) {
if config.CNIBinDir == "" {
config.CNIBinDir = "/opt/cni/bin"
}
if config.CNIConfDir == "" {
config.CNIConfDir = "/etc/cni/net.d"
}
}
func kubeProxy(config *config.Agent) {
args := []string{
"--proxy-mode", "iptables",
"--healthz-bind-address", "127.0.0.1",
"--kubeconfig", config.KubeConfig,
"--cluster-cidr", config.ClusterCIDR.String(),
}
args = append(args, config.ExtraKubeletArgs...)
command := app2.NewProxyCommand()
command.SetArgs(args)
go func() {
err := command.Execute()
logrus.Fatalf("kube-proxy exited: %v", err)
}()
}
func kubelet(config *config.Agent) {
command := app.NewKubeletCommand(context.Background().Done())
logs.InitLogs()
defer logs.FlushLogs()
args := []string{
"--healthz-bind-address", "127.0.0.1",
"--read-only-port", "0",
"--allow-privileged=true",
"--cluster-domain", "cluster.local",
"--kubeconfig", config.KubeConfig,
"--eviction-hard", "imagefs.available<5%,nodefs.available<5%",
"--eviction-minimum-reclaim", "imagefs.available=10%,nodefs.available=10%",
"--feature-gates=MountPropagation=true",
"--node-ip", config.NodeIP,
"--fail-swap-on=false",
"--cgroup-root", "/k3s",
"--cgroup-driver", "cgroupfs",
"--cni-conf-dir", config.CNIConfDir,
"--cni-bin-dir", config.CNIBinDir,
}
if len(config.ClusterDNS) > 0 {
args = append(args, "--cluster-dns", config.ClusterDNS.String())
}
if config.RuntimeSocket != "" {
args = append(args, "--container-runtime-endpoint", config.RuntimeSocket)
}
if config.ListenAddress != "" {
args = append(args, "--address", config.ListenAddress)
}
if config.CACertPath != "" {
args = append(args, "--anonymous-auth=false", "--client-ca-file", config.CACertPath)
}
if config.NodeName != "" {
args = append(args, "--hostname-override", config.NodeName)
}
args = append(args, config.ExtraKubeletArgs...)
command.SetArgs(args)
go func() {
logrus.Fatalf("kubelet exited: %v", command.Execute())
}()
}

View File

@ -0,0 +1,74 @@
package config
import (
"crypto/tls"
"net"
"net/http"
"k8s.io/apiserver/pkg/authentication/authenticator"
)
type Node struct {
Docker bool
NoFlannel bool
NoCoreDNS bool
LocalAddress string
AgentConfig Agent
CACerts []byte
ServerAddress string
Certificate *tls.Certificate
}
type Agent struct {
NodeName string
ClusterCIDR net.IPNet
ClusterDNS net.IP
KubeConfig string
NodeIP string
RuntimeSocket string
ListenAddress string
CACertPath string
CNIBinDir string
CNIConfDir string
ExtraKubeletArgs []string
ExtraKubeProxyArgs []string
}
type Control struct {
AdvertisePort int
ListenPort int
ClusterIPRange *net.IPNet
ServiceIPRange *net.IPNet
DataDir string
ETCDEndpoints []string
ETCDKeyFile string
ETCDCertFile string
ETCDCAFile string
NoScheduler bool
ExtraAPIArgs []string
ExtraControllerArgs []string
ExtraSchedulerAPIArgs []string
NodeConfig Node
Runtime *ControlRuntime `json:"-"`
}
type ControlRuntime struct {
TLSCert string
TLSKey string
TLSCA string
TLSCAKey string
TokenCA string
TokenCAKey string
ServiceKey string
PasswdFile string
KubeConfigSystem string
NodeCert string
NodeKey string
ClientToken string
NodeToken string
Handler http.Handler
Tunnel http.Handler
Authenticator authenticator.Request
}

View File

@ -0,0 +1,560 @@
package control
import (
"context"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/csv"
"encoding/hex"
"fmt"
"html/template"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/rancher/rio/pkg/daemons/config"
_ "github.com/mattn/go-sqlite3" // sqlite
"github.com/sirupsen/logrus"
"k8s.io/apiserver/pkg/authentication/authenticator"
certutil "k8s.io/client-go/util/cert"
"k8s.io/kubernetes/cmd/kube-apiserver/app"
cmapp "k8s.io/kubernetes/cmd/kube-controller-manager/app"
sapp "k8s.io/kubernetes/cmd/kube-scheduler/app"
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
"k8s.io/kubernetes/pkg/master"
_ "k8s.io/kubernetes/pkg/util/reflector/prometheus" // for reflector metric registration
_ "k8s.io/kubernetes/pkg/util/workqueue/prometheus" // for workqueue metric registration
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
)
var (
localhostIP = net.ParseIP("127.0.0.1")
kubeconfigTemplate = template.Must(template.New("kubeconfig").Parse(`apiVersion: v1
clusters:
- cluster:
server: {{.URL}}
certificate-authority-data: {{.CACert}}
name: local
contexts:
- context:
cluster: local
namespace: default
user: user
name: Default
current-context: Default
kind: Config
preferences: {}
users:
- name: user
user:
username: {{.User}}
password: {{.Password}}
`))
)
func Server(ctx context.Context, cfg *config.Control) error {
rand.Seed(time.Now().UTC().UnixNano())
runtime := &config.ControlRuntime{}
cfg.Runtime = runtime
if err := prepare(cfg, runtime); err != nil {
return err
}
cfg.Runtime.Tunnel = setupTunnel()
auth, handler, err := apiServer(ctx, cfg, runtime)
if err != nil {
return err
}
runtime.Handler = handler
runtime.Authenticator = auth
if !cfg.NoScheduler {
scheduler(cfg, runtime)
}
controllerManager(cfg, runtime)
return nil
}
func controllerManager(config *config.Control, runtime *config.ControlRuntime) {
args := []string{
"--kubeconfig", runtime.KubeConfigSystem,
"--leader-elect=true",
"--service-account-private-key-file", runtime.ServiceKey,
"--allocate-node-cidrs",
"--cluster-cidr", config.ClusterIPRange.String(),
"--root-ca-file", runtime.TokenCA,
"--port", "0",
"--secure-port", "0",
}
args = append(args, config.ExtraControllerArgs...)
command := cmapp.NewControllerManagerCommand()
command.SetArgs(args)
go func() {
logrus.Infof("Running kube-controller-manager %s", argString(args))
logrus.Fatalf("controller-manager exited: %v", command.Execute())
}()
}
type argString []string
func (a argString) String() string {
b := strings.Builder{}
for _, s := range a {
if b.Len() > 0 {
b.WriteString(" ")
}
b.WriteString(s)
}
return b.String()
}
func scheduler(config *config.Control, runtime *config.ControlRuntime) {
args := []string{
"--kubeconfig", runtime.KubeConfigSystem,
"--port", "0",
"--secure-port", "0",
}
args = append(args, config.ExtraSchedulerAPIArgs...)
command := sapp.NewSchedulerCommand()
command.SetArgs(args)
go func() {
logrus.Infof("Running kube-scheduler %s", argString(args))
logrus.Fatalf("scheduler exited: %v", command.Execute())
}()
}
func apiServer(ctx context.Context, config *config.Control, runtime *config.ControlRuntime) (authenticator.Request, http.Handler, error) {
var args []string
if len(config.ETCDEndpoints) > 0 {
args = append(args, "--storage-backend", "etcd3")
args = append(args, "--etcd-servers", strings.Join(config.ETCDEndpoints, ","))
if config.ETCDKeyFile != "" {
args = append(args, "--etcd-keyfile", config.ETCDKeyFile)
}
if config.ETCDCAFile != "" {
args = append(args, "--etcd-cafile", config.ETCDCAFile)
}
if config.ETCDCertFile != "" {
args = append(args, "--etcd-certfile", config.ETCDCertFile)
}
}
args = append(args, "--allow-privileged=true")
args = append(args, "--authorization-mode", strings.Join([]string{modes.ModeNode, modes.ModeRBAC}, ","))
args = append(args, "--service-account-signing-key-file", runtime.ServiceKey)
args = append(args, "--service-cluster-ip-range", config.ServiceIPRange.String())
args = append(args, "--advertise-port", strconv.Itoa(config.AdvertisePort))
args = append(args, "--advertise-address", localhostIP.String())
args = append(args, "--insecure-port", "0")
args = append(args, "--secure-port", strconv.Itoa(config.ListenPort))
args = append(args, "--bind-address", localhostIP.String())
args = append(args, "--tls-cert-file", runtime.TLSCert)
args = append(args, "--tls-private-key-file", runtime.TLSKey)
args = append(args, "--service-account-key-file", runtime.ServiceKey)
args = append(args, "--service-account-issuer", "k3s")
args = append(args, "--api-audiences", "unknown")
args = append(args, "--basic-auth-file", runtime.PasswdFile)
//args = append(args, "--kubelet-client-certificate", runtime.NodeCert)
//args = append(args, "--kubelet-client-key", runtime.NodeKey)
args = append(args, config.ExtraAPIArgs...)
command := app.NewAPIServerCommand(ctx.Done())
command.SetArgs(args)
go func() {
logrus.Infof("Running kube-apiserver %s", argString(args))
logrus.Fatalf("apiserver exited: %v", command.Execute())
}()
startupConfig := <-app.StartupConfig
return startupConfig.Authenticator, startupConfig.Handler, nil
}
func defaults(config *config.Control) {
if config.ClusterIPRange == nil {
_, clusterIPNet, _ := net.ParseCIDR("10.42.0.0/16")
config.ClusterIPRange = clusterIPNet
}
if config.ServiceIPRange == nil {
_, serviceIPNet, _ := net.ParseCIDR("10.43.0.0/16")
config.ServiceIPRange = serviceIPNet
}
if config.AdvertisePort == 0 {
config.AdvertisePort = 6445
}
if config.ListenPort == 0 {
config.ListenPort = 6444
}
if config.DataDir == "" {
config.DataDir = "./management-state"
}
}
func prepare(config *config.Control, runtime *config.ControlRuntime) error {
var err error
defaults(config)
if _, err := os.Stat(config.DataDir); os.IsNotExist(err) {
if err := os.MkdirAll(config.DataDir, 0700); err != nil {
return err
}
} else if err != nil {
return err
}
config.DataDir, err = filepath.Abs(config.DataDir)
if err != nil {
return err
}
os.MkdirAll(path.Join(config.DataDir, "tls"), 0700)
os.MkdirAll(path.Join(config.DataDir, "cred"), 0700)
name := "localhost"
runtime.TLSCert = path.Join(config.DataDir, "tls", name+".crt")
runtime.TLSKey = path.Join(config.DataDir, "tls", name+".key")
runtime.TLSCA = path.Join(config.DataDir, "tls", "ca.crt")
runtime.TLSCAKey = path.Join(config.DataDir, "tls", "ca.key")
runtime.TokenCA = path.Join(config.DataDir, "tls", "token-ca.crt")
runtime.TokenCAKey = path.Join(config.DataDir, "tls", "token-ca.key")
runtime.ServiceKey = path.Join(config.DataDir, "tls", "service.key")
runtime.PasswdFile = path.Join(config.DataDir, "cred", "passwd")
runtime.KubeConfigSystem = path.Join(config.DataDir, "cred", "kubeconfig-system.yaml")
runtime.NodeKey = path.Join(config.DataDir, "tls", "token-node.key")
runtime.NodeCert = path.Join(config.DataDir, "tls", "token-node.crt")
regen := false
if _, err := os.Stat(runtime.TLSCA); err != nil {
regen = true
if err := genCA(runtime); err != nil {
return err
}
}
if err := genServiceAccount(runtime); err != nil {
return err
}
if err := genTLS(regen, config, runtime); err != nil {
return err
}
if err := genTokenTLS(config, runtime); err != nil {
return err
}
if err := genUsers(config, runtime); err != nil {
return err
}
return readTokens(runtime)
}
func readTokens(runtime *config.ControlRuntime) error {
f, err := os.Open(runtime.PasswdFile)
if err != nil {
return err
}
reader := csv.NewReader(f)
reader.FieldsPerRecord = -1
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return err
}
if len(record) < 2 {
continue
}
switch record[1] {
case "node":
runtime.NodeToken = "node:" + record[0]
case "admin":
runtime.ClientToken = "admin:" + record[0]
}
}
return nil
}
func genUsers(config *config.Control, runtime *config.ControlRuntime) error {
if s, err := os.Stat(runtime.PasswdFile); err == nil && s.Size() > 0 {
return nil
}
adminToken, err := getToken()
if err != nil {
return err
}
systemToken, err := getToken()
if err != nil {
return err
}
nodeToken, err := getToken()
if err != nil {
return err
}
passwd := fmt.Sprintf(`%s,admin,admin,system:masters
%s,system,system,system:masters
%s,node,node,system:masters
`, adminToken, systemToken, nodeToken)
caCertBytes, err := ioutil.ReadFile(runtime.TLSCA)
if err != nil {
return err
}
caCert := base64.StdEncoding.EncodeToString(caCertBytes)
if err := kubeConfig(runtime.KubeConfigSystem, fmt.Sprintf("https://localhost:%d", config.ListenPort), caCert,
"system", systemToken); err != nil {
return err
}
return ioutil.WriteFile(runtime.PasswdFile, []byte(passwd), 0600)
}
func getToken() (string, error) {
token := make([]byte, 16, 16)
_, err := cryptorand.Read(token)
if err != nil {
return "", err
}
return hex.EncodeToString(token), err
}
func genTokenTLS(config *config.Control, runtime *config.ControlRuntime) error {
regen := false
if _, err := os.Stat(runtime.TokenCA); err != nil {
regen = true
if err := genTokenCA(runtime); err != nil {
return err
}
}
_, apiServerServiceIP, err := master.DefaultServiceIPRange(*config.ServiceIPRange)
if err != nil {
return err
}
cfg := certutil.Config{
CommonName: "kubernetes",
AltNames: certutil.AltNames{
DNSNames: []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes", "localhost"},
IPs: []net.IP{net.ParseIP("127.0.0.1"), apiServerServiceIP},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
}
if _, err := os.Stat(runtime.NodeCert); err == nil && !regen {
return nil
}
caKeyBytes, err := ioutil.ReadFile(runtime.TokenCAKey)
if err != nil {
return err
}
caBytes, err := ioutil.ReadFile(runtime.TokenCA)
if err != nil {
return err
}
caKey, err := certutil.ParsePrivateKeyPEM(caKeyBytes)
if err != nil {
return err
}
caCert, err := certutil.ParseCertsPEM(caBytes)
if err != nil {
return err
}
key, err := certutil.NewPrivateKey()
if err != nil {
return err
}
cert, err := certutil.NewSignedCert(cfg, key, caCert[0], caKey.(*rsa.PrivateKey))
if err != nil {
return err
}
if err := certutil.WriteKey(runtime.NodeKey, certutil.EncodePrivateKeyPEM(key)); err != nil {
return err
}
return certutil.WriteCert(runtime.NodeCert, append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...))
}
func genTLS(regen bool, config *config.Control, runtime *config.ControlRuntime) error {
if !regen {
_, certErr := os.Stat(runtime.TLSCert)
_, keyErr := os.Stat(runtime.TLSKey)
if certErr == nil && keyErr == nil {
return nil
}
}
_, apiServerServiceIP, err := master.DefaultServiceIPRange(*config.ServiceIPRange)
if err != nil {
return err
}
cfg := certutil.Config{
CommonName: "localhost",
AltNames: certutil.AltNames{
DNSNames: []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes", "localhost"},
IPs: []net.IP{apiServerServiceIP, localhostIP},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
caKeyBytes, err := ioutil.ReadFile(runtime.TLSCAKey)
if err != nil {
return err
}
caBytes, err := ioutil.ReadFile(runtime.TLSCA)
if err != nil {
return err
}
caKey, err := certutil.ParsePrivateKeyPEM(caKeyBytes)
if err != nil {
return err
}
caCert, err := certutil.ParseCertsPEM(caBytes)
if err != nil {
return err
}
key, err := certutil.NewPrivateKey()
if err != nil {
return err
}
cert, err := certutil.NewSignedCert(cfg, key, caCert[0], caKey.(*rsa.PrivateKey))
if err != nil {
return err
}
if err := certutil.WriteKey(runtime.TLSKey, certutil.EncodePrivateKeyPEM(key)); err != nil {
return err
}
return certutil.WriteCert(runtime.TLSCert, append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...))
}
func genServiceAccount(runtime *config.ControlRuntime) error {
_, keyErr := os.Stat(runtime.ServiceKey)
if keyErr == nil {
return nil
}
key, err := certutil.NewPrivateKey()
if err != nil {
return err
}
return certutil.WriteKey(runtime.ServiceKey, certutil.EncodePrivateKeyPEM(key))
}
func genTokenCA(runtime *config.ControlRuntime) error {
caKey, err := certutil.NewPrivateKey()
if err != nil {
return err
}
cfg := certutil.Config{
CommonName: fmt.Sprintf("%s-ca@%d", "k3s-token", time.Now().Unix()),
}
cert, err := certutil.NewSelfSignedCACert(cfg, caKey)
if err != nil {
return err
}
if err := certutil.WriteKey(runtime.TokenCAKey, certutil.EncodePrivateKeyPEM(caKey)); err != nil {
return err
}
return certutil.WriteCert(runtime.TokenCA, certutil.EncodeCertPEM(cert))
}
func genCA(runtime *config.ControlRuntime) error {
caKey, err := certutil.NewPrivateKey()
if err != nil {
return err
}
cfg := certutil.Config{
CommonName: fmt.Sprintf("%s-ca@%d", "k3s", time.Now().Unix()),
}
cert, err := certutil.NewSelfSignedCACert(cfg, caKey)
if err != nil {
return err
}
if err := certutil.WriteKey(runtime.TLSCAKey, certutil.EncodePrivateKeyPEM(caKey)); err != nil {
return err
}
return certutil.WriteCert(runtime.TLSCA, certutil.EncodeCertPEM(cert))
}
func kubeConfig(dest, url, cert, user, password string) error {
data := struct {
URL string
CACert string
User string
Password string
}{
URL: url,
CACert: cert,
User: user,
Password: password,
}
output, err := os.Create(dest)
if err != nil {
return err
}
defer output.Close()
return kubeconfigTemplate.Execute(output, &data)
}

View File

@ -0,0 +1,50 @@
package control
import (
"context"
"net"
"net/http"
"time"
"github.com/rancher/norman/pkg/kv"
"github.com/rancher/norman/pkg/remotedialer"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/cmd/kube-apiserver/app"
)
func setupTunnel() http.Handler {
tunnelServer := remotedialer.New(authorizer, remotedialer.DefaultErrorWriter)
setupProxyDialer(tunnelServer)
return tunnelServer
}
func setupProxyDialer(tunnelServer *remotedialer.Server) {
app.DefaultProxyDialerFn = utilnet.DialFunc(func(_ context.Context, network, address string) (net.Conn, error) {
_, port, _ := net.SplitHostPort(address)
addr := "127.0.0.1"
if port != "" {
addr += ":" + port
}
nodeName, _ := kv.Split(address, ":")
return tunnelServer.Dial(nodeName, 15*time.Second, "tcp", addr)
})
}
func authorizer(req *http.Request) (clientKey string, authed bool, err error) {
user, ok := request.UserFrom(req.Context())
if !ok {
return "", false, nil
}
if user.GetName() != "node" {
return "", false, nil
}
nodeName := req.Header.Get("X-K3s-NodeName")
if nodeName == "" {
return "", false, nil
}
return nodeName, true, nil
}

254
pkg/enterchroot/enter.go Normal file
View File

@ -0,0 +1,254 @@
package enterchroot
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/reexec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
losetup "gopkg.in/freddierice/go-losetup.v1"
)
const (
magic = "_SQMAGIC_"
)
var (
symlinks = []string{"lib", "bin", "sbin", "lib64"}
)
func init() {
reexec.Register("enter-root", enter)
}
func enter() {
if os.Getenv("ENTER_DEBUG") == "true" {
logrus.SetLevel(logrus.DebugLevel)
}
logrus.Debug("Running bootstrap")
err := run(os.Getenv("ENTER_DATA"))
if err != nil {
logrus.Fatal(err)
}
}
func Mount(dataDir string, stdout, stderr io.Writer, args []string) error {
if logrus.GetLevel() >= logrus.DebugLevel {
os.Setenv("ENTER_DEBUG", "true")
}
root, offset, err := findRoot()
if err != nil {
return err
}
os.Setenv("ENTER_DATA", dataDir)
os.Setenv("ENTER_ROOT", root)
logrus.Debugf("Using data [%s] root [%s]", dataDir, root)
stat, err := os.Stat(root)
if err != nil {
return fmt.Errorf("failed to find %s: %v", root, err)
}
if !stat.IsDir() {
logrus.Debugf("Attaching file [%s] offset [%d]", root, offset)
dev, err := losetup.Attach(root, offset, true)
if err != nil {
return errors.Wrap(err, "creating loopback device")
}
defer dev.Detach()
os.Setenv("ENTER_DEVICE", dev.Path())
go func() {
// Assume that after 3 seconds loop back device has been mounted
time.Sleep(3 * time.Second)
info, err := dev.GetInfo()
if err != nil {
return
}
info.Flags |= losetup.FlagsAutoClear
err = dev.SetInfo(info)
if err != nil {
return
}
}()
}
logrus.Debugf("Running enter-root %v", args)
cmd := &exec.Cmd{
Path: reexec.Self(),
Args: append([]string{"enter-root"}, args...),
SysProcAttr: &syscall.SysProcAttr{
//Cloneflags: syscall.CLONE_NEWPID | syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
Unshareflags: syscall.CLONE_NEWNS,
Pdeathsig: syscall.SIGKILL,
},
Stdout: stdout,
Stdin: os.Stdin,
Stderr: stderr,
Env: os.Environ(),
}
return cmd.Run()
}
func findRoot() (string, uint64, error) {
root := os.Getenv("ENTER_ROOT")
if root != "" {
return root, 0, nil
}
for _, suffix := range []string{".root", ".squashfs"} {
test := os.Args[0] + suffix
if _, err := os.Stat(test); err == nil {
return test, 0, nil
}
}
return inFile()
}
func inFile() (string, uint64, error) {
f, err := os.Open(reexec.Self())
if err != nil {
return "", 0, err
}
defer f.Close()
buf := make([]byte, 8192)
test := []byte(strings.ToLower(magic))
testLength := len(test)
offset := uint64(0)
found := 0
for {
n, err := f.Read(buf)
if err == io.EOF && n == 0 {
break
} else if err != nil {
return "", 0, err
}
for _, b := range buf[:n] {
if b == test[found] {
found++
if found == testLength {
return reexec.Self(), offset + 1, nil
}
} else {
found = 0
}
offset++
}
}
return "", 0, fmt.Errorf("failed to find image in file %s", os.Args[0])
}
func run(data string) error {
os.MkdirAll(data, 0755)
if err := mount.Mount("tmpfs", data, "tmpfs", ""); err != nil {
return errors.Wrapf(err, "remounting data %s", data)
}
root := os.Getenv("ENTER_ROOT")
device := os.Getenv("ENTER_DEVICE")
logrus.Debugf("Using root %s %s", root, device)
usr := filepath.Join(data, "usr")
dotRoot := filepath.Join(data, ".root")
for _, d := range []string{usr, dotRoot} {
if err := os.MkdirAll(d, 0755); err != nil {
return fmt.Errorf("failed to make dir %s: %v", data, err)
}
}
if device == "" {
logrus.Debugf("Bind mounting %s to %s", root, usr)
if err := mount.Mount(root, usr, "none", "bind"); err != nil {
return fmt.Errorf("failed to bind mount")
}
} else {
logrus.Debugf("Mounting squashfs %s to %s", device, usr)
squashErr := checkSquashfs()
if err := mount.Mount(device, usr, "squashfs", "ro"); err != nil {
err = errors.Wrap(err, "mounting squashfs")
if squashErr != nil {
err = errors.Wrap(err, squashErr.Error())
}
return err
}
}
if err := os.Chdir(data); err != nil {
return err
}
for _, p := range symlinks {
if _, err := os.Lstat(p); os.IsNotExist(err) {
if err := os.Symlink(filepath.Join("usr", p), p); err != nil {
return errors.Wrapf(err, "failed to symlink %s", p)
}
}
}
logrus.Debugf("pivoting to . .root")
if err := syscall.PivotRoot(".", ".root"); err != nil {
return errors.Wrap(err, "pivot_root failed")
}
if err := mount.ForceMount("", ".", "none", "rprivate"); err != nil {
return errors.Wrapf(err, "making . private %s", data)
}
if err := syscall.Chroot("/"); err != nil {
return err
}
if err := os.Chdir("/"); err != nil {
return err
}
if _, err := os.Stat("/usr/init"); err != nil {
return errors.Wrap(err, "failed to find /usr/init")
}
return syscall.Exec("/usr/init", os.Args, os.Environ())
}
func checkSquashfs() error {
if !inProcFS() {
exec.Command("modprobe", "squashfs").Run()
}
if !inProcFS() {
return errors.New("This kernel does not support squashfs, please enable. " +
"On Fedora you may need to run \"dnf install kernel-modules-$(uname -r)\"")
}
return nil
}
func inProcFS() bool {
bytes, err := ioutil.ReadFile("/proc/filesystems")
if err != nil {
logrus.Errorf("Failed to read /proc/filesystems: %v", err)
return false
}
return strings.Contains(string(bytes), "squashfs")
}

52
pkg/kubectl/main.go Normal file
View File

@ -0,0 +1,52 @@
package kubectl
import (
goflag "flag"
"fmt"
"math/rand"
"os"
"time"
"github.com/docker/docker/pkg/reexec"
"github.com/rancher/rio/pkg/server"
"github.com/spf13/pflag"
utilflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/apiserver/pkg/util/logs"
"k8s.io/kubernetes/pkg/kubectl/cmd"
)
func init() {
reexec.Register("kubectl", Main)
}
func Main() {
kubenv := os.Getenv("KUBECONFIG")
if kubenv == "" {
config, err := server.HomeKubeConfig()
if _, serr := os.Stat(config); err == nil && serr == nil {
os.Setenv("KUBECONFIG", config)
}
}
main()
}
func main() {
rand.Seed(time.Now().UnixNano())
command := cmd.NewDefaultKubectlCommand()
// TODO: once we switch everything over to Cobra commands, we can go back to calling
// utilflag.InitFlags() (by removing its pflag.Parse() call). For now, we have to set the
// normalize func and add the go flag set by hand.
pflag.CommandLine.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
// utilflag.InitFlags()
logs.InitLogs()
defer logs.FlushLogs()
if err := command.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}

41
pkg/server/auth.go Normal file
View File

@ -0,0 +1,41 @@
package server
import (
"net/http"
"github.com/gorilla/mux"
"github.com/rancher/rio/pkg/daemons/config"
"github.com/sirupsen/logrus"
"k8s.io/apiserver/pkg/endpoints/request"
)
func doAuth(serverConfig *config.Control, next http.Handler, rw http.ResponseWriter, req *http.Request) {
if serverConfig == nil || serverConfig.Runtime.Authenticator == nil {
next.ServeHTTP(rw, req)
return
}
resp, ok, err := serverConfig.Runtime.Authenticator.AuthenticateRequest(req)
if err != nil {
logrus.Errorf("failed to authenticate request: %v", err)
rw.WriteHeader(http.StatusInternalServerError)
return
}
if !ok || resp.User.GetName() != "node" {
rw.WriteHeader(http.StatusUnauthorized)
return
}
ctx := request.WithUser(req.Context(), resp.User)
req = req.WithContext(ctx)
next.ServeHTTP(rw, req)
}
func authMiddleware(serverConfig *config.Control) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
doAuth(serverConfig, next, rw, req)
})
}
}

72
pkg/server/router.go Normal file
View File

@ -0,0 +1,72 @@
package server
import (
"net/http"
"github.com/rancher/rio/pkg/daemons/config"
"k8s.io/apimachinery/pkg/util/json"
"github.com/gorilla/mux"
)
type CACertsGetter func() (string, error)
func router(serverConfig *config.Control, tunnel http.Handler, cacertsGetter CACertsGetter) http.Handler {
authed := mux.NewRouter()
authed.Use(authMiddleware(serverConfig))
authed.NotFoundHandler = serverConfig.Runtime.Handler
authed.Path("/v1-k3s/connect").Handler(tunnel)
authed.Path("/v1-k3s/node.crt").Handler(nodeCrt(serverConfig))
authed.Path("/v1-k3s/node.key").Handler(nodeKey(serverConfig))
authed.Path("/v1-k3s/config").Handler(configHandler(serverConfig))
router := mux.NewRouter()
router.NotFoundHandler = authed
router.Path("/cacerts").Handler(cacerts(cacertsGetter))
return router
}
func cacerts(getter CACertsGetter) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
content, err := getter()
if err != nil {
resp.WriteHeader(http.StatusInternalServerError)
resp.Write([]byte(err.Error()))
}
resp.Header().Set("content-type", "text/plain")
resp.Write([]byte(content))
})
}
func nodeCrt(server *config.Control) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if req.TLS == nil {
resp.WriteHeader(http.StatusNotFound)
return
}
http.ServeFile(resp, req, server.Runtime.NodeCert)
})
}
func nodeKey(server *config.Control) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if req.TLS == nil {
resp.WriteHeader(http.StatusNotFound)
return
}
http.ServeFile(resp, req, server.Runtime.NodeKey)
})
}
func configHandler(server *config.Control) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if req.TLS == nil {
resp.WriteHeader(http.StatusNotFound)
return
}
resp.Header().Set("content-type", "application/json")
json.NewEncoder(resp).Encode(server)
})
}

230
pkg/server/server.go Normal file
View File

@ -0,0 +1,230 @@
package server
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
"github.com/rancher/norman"
"github.com/rancher/norman/pkg/clientaccess"
"github.com/rancher/norman/pkg/dynamiclistener"
"github.com/rancher/norman/pkg/resolvehome"
"github.com/rancher/norman/types"
"github.com/rancher/rio/pkg/daemons/config"
"github.com/rancher/rio/pkg/daemons/control"
"github.com/rancher/rio/pkg/tls"
v1 "github.com/rancher/rio/types/apis/k3s.cattle.io/v1"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/net"
)
func resolveDataDir(dataDir string) (string, error) {
if dataDir == "" {
if os.Getuid() == 0 {
dataDir = "/var/lib/rancher/k3s"
} else {
dataDir = "${HOME}/.rancher/k3s"
}
}
dataDir = filepath.Join(dataDir, "server")
return resolvehome.Resolve(dataDir)
}
func StartServer(ctx context.Context, config *Config) error {
if err := setupDataDirAndChdir(&config.ControlConfig); err != nil {
return err
}
if err := control.Server(ctx, &config.ControlConfig); err != nil {
return errors.Wrap(err, "starting kubernetes")
}
certs, err := startNorman(ctx, &config.TLSConfig, &config.ControlConfig)
if err != nil {
return errors.Wrap(err, "starting tls server")
}
printTokens(certs, config.ControlConfig.NodeConfig.AgentConfig.NodeIP, &config.TLSConfig, &config.ControlConfig)
writeKubeConfig(certs, &config.TLSConfig, &config.ControlConfig)
return nil
}
func startNorman(ctx context.Context, tlsConfig *dynamiclistener.UserConfig, config *config.Control) (string, error) {
var (
err error
tlsServer dynamiclistener.ServerInterface
)
tlsConfig.Handler = router(config, config.Runtime.Tunnel, func() (string, error) {
if tlsServer == nil {
return "", nil
}
return tlsServer.CACert()
})
normanConfig := &norman.Config{
Name: "k3s",
KubeConfig: config.Runtime.KubeConfigSystem,
Clients: []norman.ClientFactory{
v1.Factory,
},
Schemas: []*types.Schemas{
v1.Schemas,
},
CRDs: map[*types.APIVersion][]string{
&v1.APIVersion: {
v1.ListenerConfigGroupVersionKind.Kind,
},
},
IgnoredKubeConfigEnv: true,
GlobalSetup: func(ctx context.Context) (context.Context, error) {
tlsServer, err = tls.NewServer(ctx, v1.ClientsFrom(ctx).ListenerConfig, *tlsConfig)
return ctx, err
},
}
ctx, _, err = normanConfig.Build(ctx, nil)
if err != nil {
return "", err
}
for {
certs, err := tlsServer.CACert()
if err != nil {
logrus.Infof("waiting to generate CA certs")
time.Sleep(time.Second)
continue
}
return certs, nil
}
}
func HomeKubeConfig() (string, error) {
return resolvehome.Resolve("${HOME}/.kube/k3s.yaml")
}
func printTokens(certs, advertiseIP string, tlsConfig *dynamiclistener.UserConfig, config *config.Control) {
var (
nodeFile string
)
if advertiseIP == "" {
advertiseIP = "localhost"
}
if len(config.Runtime.NodeToken) > 0 {
p := filepath.Join(config.DataDir, "node-token")
if err := writeToken(config.Runtime.NodeToken, p, certs); err == nil {
logrus.Infof("Node token is available at %s", p)
nodeFile = p
}
}
if len(nodeFile) > 0 {
printToken(tlsConfig.HTTPSPort, advertiseIP, "To join node to cluster:", nodeFile, "agent")
}
}
func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config *config.Control) {
clientToken := FormatToken(config.Runtime.ClientToken, certs)
url := fmt.Sprintf("https://localhost:%d", tlsConfig.HTTPSPort)
kubeConfig, err := HomeKubeConfig()
def := true
if err != nil {
kubeConfig = filepath.Join(config.DataDir, "kubeconfig-k3s.yaml")
def = false
}
if err = clientaccess.AgentAccessInfoToKubeConfig(kubeConfig, url, clientToken); err != nil {
logrus.Errorf("Failed to generate kubeconfig: %v", err)
}
logrus.Infof("Wrote kubeconfig %s", kubeConfig)
if def {
logrus.Infof("Run: %s kubectl", filepath.Base(os.Args[0]))
}
}
func setupDataDirAndChdir(config *config.Control) error {
var (
err error
)
config.DataDir, err = resolveDataDir(config.DataDir)
if err != nil {
return err
}
dataDir := config.DataDir
if err := os.MkdirAll(dataDir, 0700); err != nil {
return errors.Wrapf(err, "can not mkdir %s", dataDir)
}
if err := os.Chdir(dataDir); err != nil {
return errors.Wrapf(err, "can not chdir %s", dataDir)
}
return nil
}
func readTokenFile(file string) (string, error) {
content, err := ioutil.ReadFile(file)
if err != nil {
return "", err
}
return strings.TrimSpace(string(content)), nil
}
func printToken(httpsPort int, advertiseIP, prefix, file, cmd string) {
token, err := readTokenFile(file)
if err != nil {
logrus.Error(err)
}
ip := advertiseIP
if ip == "" {
hostIP, err := net.ChooseHostInterface()
if err != nil {
logrus.Error(err)
}
ip = hostIP.String()
}
logrus.Infof("%s rio %s -s https://%s:%d -t %s", prefix, cmd, ip, httpsPort, token)
}
func FormatToken(token string, certs string) string {
if len(token) == 0 {
return token
}
prefix := "K10"
if len(certs) > 0 {
digest := sha256.Sum256([]byte(certs))
prefix = "K10" + hex.EncodeToString(digest[:]) + "::"
}
return prefix + token
}
func writeToken(token, file, certs string) error {
if len(token) == 0 {
return nil
}
token = FormatToken(token, certs)
return ioutil.WriteFile(file, []byte(token+"\n"), 0600)
}

12
pkg/server/types.go Normal file
View File

@ -0,0 +1,12 @@
package server
import (
"github.com/rancher/norman/pkg/dynamiclistener"
"github.com/rancher/rio/pkg/daemons/config"
)
type Config struct {
DisableAgent bool
TLSConfig dynamiclistener.UserConfig
ControlConfig config.Control
}

79
pkg/tls/storage.go Normal file
View File

@ -0,0 +1,79 @@
package tls
import (
"context"
"github.com/rancher/norman/pkg/dynamiclistener"
v1 "github.com/rancher/rio/types/apis/k3s.cattle.io/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
const (
ns = "kube-system"
name = "tls-config"
)
func NewServer(ctx context.Context, listenerClient v1.ListenerConfigClient, config dynamiclistener.UserConfig) (dynamiclistener.ServerInterface, error) {
storage := &listenerConfigStorage{
client: listenerClient,
cache: listenerClient.Cache(),
}
server, err := dynamiclistener.NewServer(storage, config)
listenerClient.OnChange(ctx, "listen-config", func(obj *v1.ListenerConfig) (runtime.Object, error) {
return obj, server.Update(fromStorage(obj))
})
return server, err
}
type listenerConfigStorage struct {
cache v1.ListenerConfigClientCache
client v1.ListenerConfigClient
}
func (l *listenerConfigStorage) Set(config *dynamiclistener.ListenerStatus) (*dynamiclistener.ListenerStatus, error) {
if config == nil {
return nil, nil
}
obj, err := l.cache.Get(ns, name)
if errors.IsNotFound(err) {
ls := v1.NewListenerConfig(ns, name, v1.ListenerConfig{
Status: *config,
})
ls, err := l.client.Create(ls)
return fromStorage(ls), err
} else if err != nil {
return nil, err
}
obj = obj.DeepCopy()
obj.ResourceVersion = config.Revision
obj.Status = *config
obj.Status.Revision = ""
obj, err = l.client.Update(obj)
return fromStorage(obj), err
}
func (l *listenerConfigStorage) Get() (*dynamiclistener.ListenerStatus, error) {
obj, err := l.cache.Get(ns, name)
if errors.IsNotFound(err) {
obj, err = l.client.Get(ns, name, metav1.GetOptions{})
}
return fromStorage(obj), err
}
func fromStorage(obj *v1.ListenerConfig) *dynamiclistener.ListenerStatus {
if obj == nil {
return nil
}
copy := obj.DeepCopy()
copy.Status.Revision = obj.ResourceVersion
return &copy.Status
}

31
scripts/build Executable file
View File

@ -0,0 +1,31 @@
#!/bin/bash
set -e
source $(dirname $0)/version
cd $(dirname $0)/..
LDFLAGS="-X github.com/rancher/rio/version.Version=$VERSION -w -s"
STATIC="-extldflags -static"
STATIC_SQLITE="-extldflags '-static -lm -ldl -lz -lpthread'"
cross()
{
echo Building windows CLI
GOOS=windows GOARCH=amd64 go build -ldflags "$LDFLAGS" -o bin/rio-windows ./cli/main.go
echo Building mac CLI
GOOS=darwin GOARCH=amd64 go build -ldflags "$LDFLAGS" -o bin/rio-darwin ./cli/main.go
}
mkdir -p bin
if [ -n "$CROSS" ]; then
cross
fi
echo Building incluster
GOOS=linux GOARCH=amd64
CGO_ENABLED=0 go build -ldflags "$LDFLAGS $STATIC" -o bin/rio-incluster
echo Building cli full
CGO_ENABLED=1 go build -tags "static_build libsqlite3 k8s ctr no_etcd netgo osusergo" -ldflags "$LDFLAGS $STATIC_SQLITE" -o bin/rio-full ./cli/main.go

9
scripts/ci Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
set -e
cd $(dirname $0)
./validate
./build
./package
./test

14
scripts/dev-agent.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
set -e
cd $(dirname $0)/../bin
# Prime sudo
sudo echo Compiling CLI
go build -tags "k8s no_etcd" -o rio-agent ../cli/main.go
echo Building image and agent
../image/build
echo Running
exec sudo ENTER_ROOT=../image/main.squashfs ./rio-agent --debug agent -s https://localhost:7443 -t $(<${HOME}/.rancher/rio/server/node-token)

7
scripts/dev-k8s-only-server.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
set -e
cd $(dirname $0)/../bin
echo Running
go run -tags "k8s no_etcd" ../cli/main.go --debug server --disable-controllers --disable-agent

6
scripts/dev-login.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
set -e
cd $(dirname $0)/../bin
rio login -s https://localhost:5443 -t $(<${HOME}/.rancher/rio/server/client-token)

7
scripts/dev-server.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
set -e
cd $(dirname $0)/../bin
echo Running
go run -tags k3s ../cli/main.go --debug server --disable-agent

11
scripts/entry Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
set -e
mkdir -p bin dist
if [ -e ./scripts/$1 ]; then
./scripts/"$@"
else
exec "$@"
fi
chown -R $DAPPER_UID:$DAPPER_GID .

8
scripts/package Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
cd $(dirname $0)
./package-cli
./package-image
./package-tar

11
scripts/package-cli Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
set -e
source $(dirname $0)/version
cd $(dirname $0)/../image
./build
cp ../bin/rio-full ../bin/rio
echo -n "_sqmagic_" >> ../bin/rio
cat main.squashfs >> ../bin/rio

17
scripts/package-image Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
set -e
source $(dirname $0)/version
cd $(dirname $0)/../package
TAG=${TAG:-${VERSION}${SUFFIX}}
REPO=${REPO:-rancher}
cp ../bin/rio ./rio
IMAGE=${REPO}/rio:${TAG}
docker build -t ${IMAGE} .
mkdir -p ../dist
echo ${IMAGE} > ../dist/images
echo Built ${IMAGE}

17
scripts/package-tar Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
cd $(dirname $0)/..
. ./scripts/version
mkdir -p dist/artifacts
tar cvzf dist/artifacts/rio-${VERSION}-linux-amd64.tar.gz -h bin/rio --xform='s!^bin!rio-'${VERSION}'-linux-amd64!'
tar cvzf dist/artifacts/rio-${VERSION}-darwin.tar.gz bin/rio-darwin --xform='s!.*!rio-'${VERSION}'-darwin/rio!'
W=rio-${VERSION}-windows
mkdir -p $W
trap "rm -rf $W" EXIT
cp -f bin/rio-windows $W/rio.exe
zip dist/artifacts/rio-${VERSION}-windows.zip $W/rio.exe

3
scripts/release Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
exec $(dirname $0)/ci

16
scripts/symlink-k8s.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
if [ ! -d $1/staging/src/k8s.io ]; then
echo Kubernetes source not found at $1
exit 1
fi
cd $(dirname $0)/../vendor/k8s.io
for i in $1/staging/src/k8s.io/*; do
rm -rf $(basename $i)
ln -s $i .
done
rm -rf kubernetes
mkdir -p kubernetes
cd kubernetes
ln -s $1/{cmd,pkg,plugin,third_party} .

50
scripts/test Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
set -e
cd $(dirname $0)/..
echo Running tests
mkdir -p ./build
mkdir -p /var/lib/rancher/rio/agent
mount -t tmpfs none /var/lib/rancher/rio/agent
./bin/rio server --disable-agent | grep -v level=info > /var/lib/rancher/rio/agent/agent.log 2>&1 &
for i in {1..120}; do
if [ ! -e /var/lib/rancher/rio/server/node-token ]; then
sleep .5
continue
fi
curl -sf http://localhost:7080/healthz >/dev/null && break
sleep .5
done
curl -sf http://localhost:7080/healthz >/dev/null
rm -rf ./image/root
unsquashfs -d ./image/root ./image/main.squashfs
ENTER_ROOT=$(pwd)/image/root ./bin/rio --debug agent -s https://localhost:7443 -t $(</var/lib/rancher/rio/server/node-token) >>/var/lib/rancher/rio/agent/agent.log 2>&1 &
export PATH=$(pwd)/bin:$PATH
rio login -s https://localhost:7443 -t /var/lib/rancher/rio/server/client-token
echo Waiting for istio/istio-gateway
rio --project=rio-system wait istio/istio-gateway
rio --project=rio-system ps
rio --project=rio-system ps -c
chmod +x ./tests/init-nfs.bash
./tests/init-nfs.bash
export RUN_NFS_TEST=true
cd ./tests
tox -- -n $(nproc)
cd ..
bats -r ./tests || {
tail -n 100 /var/lib/rancher/rio/agent/agent.log
exit 1
}

29
scripts/validate Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
set -e
cd $(dirname $0)/..
echo Running validation
if grep -r '^[[:space:]]*\[\[.*]][[:space:]]*$' tests; then
echo "Add \"|| false\" to bats tests in any place you use [[ ... ]]"
exit 1
fi
PACKAGES="$(go list ./...)"
echo Running: go vet
go vet ${PACKAGES}
echo Running: gometalinter
for i in ${PACKAGES}; do
if [ -n "$(gometalinter $i | \
grep -v 'should have comment.*or be unexported' | \
grep -v 'cli/cmd.*don.t use underscores in Go name' | \
grep -v 'cli/cmd.*should be DNS' | \
tee /dev/stderr)" ]; then
failed=true
fi
done
test -z "$failed"
echo Running: go fmt
test -z "$(go fmt ${PACKAGES} | tee /dev/stderr)"

18
scripts/version Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
DIRTY="-dirty"
fi
COMMIT=$(git rev-parse --short HEAD)
GIT_TAG=${DRONE_TAG:-$(git tag -l --contains HEAD | head -n 1)}
if [[ -z "$DIRTY" && -n "$GIT_TAG" ]]; then
VERSION=$GIT_TAG
else
VERSION="${COMMIT}${DIRTY}"
fi
if [ -z "$ARCH" ]; then
ARCH=amd64
fi

246
trash.lock Executable file
View File

@ -0,0 +1,246 @@
package: package=github.com/rancher/rio
import:
- package: github.com/coreos/flannel
version: 39af3d7e46f2efa156644e247bdcf3b5bc5f1394
- package: github.com/gorilla/mux
version: v1.6.2
- package: github.com/gorilla/websocket
version: v1.2.0
- package: github.com/mattn/go-sqlite3
version: v1.9.0
- package: github.com/natefinch/lumberjack
version: aee4629129445bbdfb69aa565537dcfa16544311
- package: github.com/rancher/norman
version: 5726ebfba191eef161af9638e85bf4fcaa58e008
repo: https://github.com/ibuildthecloud/norman.git
- package: github.com/urfave/cli
version: 8e01ec4cd3e2d84ab2fe90d8210528ffbb06d8ff
- package: golang.org/x/crypto
version: a49355c7e3f8fe157a85be2f77e6e269a0f89602
- package: golang.org/x/sync
version: 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
- package: gopkg.in/freddierice/go-losetup.v1
version: fc9adea44124401d8bfef3a97eaf61b5d44cc2c6
- package: k8s.io/kubernetes
version: a1d7d1b140f43b6503311ffc1dd80017c553bf8e
repo: file:///home/darren/src/kuberlite/.git
transitive: true
staging: true
- package: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- package: github.com/prometheus/client_model
version: model-0.0.2-12-gfa8ad6fec33561
- package: github.com/prometheus/common
version: 13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207
- package: github.com/golang/protobuf
version: v1.1.0
- package: golang.org/x/sys
version: 95c6576299259db960f6c5b9b69ea52422860fce
- package: github.com/ugorji/go
version: bdcc60b419d136a85cdf2e7cbcac34b3f1cd6e57
- package: github.com/vishvananda/netns
version: be1fbeda19366dea804f00efff2dd73a1642fdcc
- package: gopkg.in/natefinch/lumberjack.v2
version: v1.0-16-g20b71e5b60d756
- package: vbom.ml/util
version: db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394
- package: github.com/exponent-io/jsonpath
version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5
- package: github.com/google/certificate-transparency-go
version: v1.0.21
- package: github.com/coreos/go-semver
version: v0.2.0-9-ge214231b295a8e
- package: github.com/docker/spdystream
version: 449fdfce4d962303d702fec724ef0ad181c92528
- package: github.com/renstrom/dedent
version: v1.0.0-3-g020d11c3b9c0c7
- package: golang.org/x/text
version: b19bf474d317b857955b12035d2c5acb57ce8b01
- package: github.com/container-storage-interface/spec
version: v1.0.0
- package: github.com/evanphx/json-patch
version: v4.0.0-3-g36442dbdb58521
- package: github.com/sigma/go-inotify
version: c87b6cf5033d2c6486046f045eeebdc3d910fd38
- package: github.com/blang/semver
version: v3.5.0
- package: github.com/docker/libnetwork
version: v0.8.0-dev.2-1265-ga9cd636e378982
- package: github.com/fatih/camelcase
version: f6a740d52f961c60348ebb109adde9f4635d7540
- package: github.com/prometheus/client_golang
version: v0.8.0-83-ge7e903064f5e9e
- package: github.com/shurcooL/sanitized_anchor_name
version: 10ef21a441db47d8b13ebcc5fd2310f636973c77
- package: github.com/spf13/cobra
version: v0.0.1-34-gc439c4fa093711
- package: github.com/docker/docker
version: docs-v1.12.0-rc4-2016-07-15-9510-ga9fbbdc8dd8794
- package: golang.org/x/net
version: 0ed95abb35c445290478a5348a7b38bb154135fd
- package: github.com/json-iterator/go
version: 1.1.4
- package: github.com/opencontainers/runc
version: v1.0.0-rc5-46-g871ba2e58e2431
- package: gopkg.in/inf.v0
version: v0.9.0
- package: github.com/containerd/containerd
version: v1.0.2
- package: github.com/ghodss/yaml
version: v1.0.0-4-gc7ce16629ff4cd
- package: github.com/ibuildthecloud/kvsql
version: 6bb3d252056655760ed8ca6557d6d5e607b361d2
- package: google.golang.org/genproto
version: 09f6ed296fc66555a25fe4ce95173148778dfa85
- package: github.com/Microsoft/hcsshim
version: v0.6.11
- package: google.golang.org/grpc
version: v1.13.0
- package: github.com/gregjones/httpcache
version: 787624de3eb7bd915c329cba748687a3b22666a6
- package: github.com/golang/groupcache
version: 02826c3e79038b59d737d3b1c0a1d937f71a4433
- package: github.com/karrick/godirwalk
version: v1.7.5
- package: github.com/mattn/go-shellwords
version: v1.0.3-20-gf8471b0a71ded0
- package: github.com/google/gofuzz
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
- package: github.com/modern-go/concurrent
version: 1.0.3
- package: github.com/google/cadvisor
version: 25bec0e2ace4846e5caaaf69991046c6f44c4bac
repo: https://github.com/ibuildthecloud/cadvisor.git
- package: sigs.k8s.io/yaml
version: v1.1.0
- package: bitbucket.org/ww/goautoneg
version: a547fc61f48d567d5b4ec6f8aee5573d8efce11d
repo: https://github.com/rancher/goautoneg.git
- package: github.com/beorn7/perks
version: 3ac7bf7a47d159a033b107610db8a1b6575507a4
- package: github.com/sirupsen/logrus
version: v1.0.3-11-g89742aefa4b206
- package: gopkg.in/square/go-jose.v2
version: v2.1.6-4-g89060dee6a84df
- package: k8s.io/utils
version: 66066c83e385e385ccc3c964b44fd7dcd413d0ed
- package: github.com/daviddengcn/go-colortext
version: 511bcaf42ccd42c38aba7427b6673277bf19e2a1
- package: github.com/prometheus/procfs
version: 65c1f6f8f0fc1e2185eb9863a3bc751496404259
- package: golang.org/x/tools
version: 2382e3994d48b1d22acc2c86bcad0a2aff028e32
- package: github.com/jteeuwen/go-bindata
version: v3.0.7-72-ga0ff2567cfb709
- package: github.com/modern-go/reflect2
version: v1.0.1
- package: golang.org/x/time
version: f51c12702a4d776e4c1fa9b0fabab841babae631
- package: k8s.io/gengo
version: 51747d6e00da1fc578d5a333a93bb2abcbce7a95
- package: github.com/euank/go-kmsg-parser
version: v2.0.0
- package: github.com/chai2010/gettext-go
version: c6fed771bfd517099caf0f7a961671fa8ed08723
- package: github.com/davecgh/go-spew
version: v1.1.0-1-g782f4967f2dc45
- package: github.com/docker/distribution
version: v2.6.0-rc.1-209-gedc3ab29cdff86
- package: github.com/docker/go-units
version: v0.3.1-11-g9e638d38cf6977
- package: github.com/mindprince/gonvml
version: fee913ce8fb235edf54739d259ca0ecc226c7b8a
- package: github.com/MakeNowJust/heredoc
version: bb23615498cded5e105af4ce27de75b089cbe851
- package: github.com/syndtr/gocapability
version: e7cb7fa329f456b3855136a2642b197bad7366ba
- package: k8s.io/klog
version: 8139d8cb77af419532b33dfa7dd09fbc5f1d344f
- package: github.com/emicklei/go-restful
version: 2.2.0-4-gff4f55a206334e
- package: github.com/google/btree
version: 7d79101e329e5a3adf994758c578dab82b90c017
- package: github.com/Azure/go-ansiterm
version: d6e3b3328b783f23731bc4d058875b0371ff8109
- package: github.com/containerd/console
version: 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
- package: github.com/pkg/errors
version: v0.8.0
- package: github.com/googleapis/gnostic
version: 0c5108395e2debce0d731cf0287ddf7242066aba
- package: github.com/imdario/mergo
version: v0.3.5
- package: github.com/robfig/cron
version: v1-53-gdf38d32658d878
- package: github.com/JeffAshton/win_pdh
version: 76bb4ee9f0ab50f77826f2a2ee7fb9d3880d6ec2
- package: github.com/armon/circbuf
version: bbbad097214e2918d8543d5201d12bfd7bca254d
- package: k8s.io/heapster
version: v1.2.0-beta.1
- package: github.com/matttproud/golang_protobuf_extensions
version: v1.0.1
- package: github.com/mrunalp/fileutils
version: 4ee1cc9a80582a0c75febdd5cfa779ee4361cbca
- package: github.com/opencontainers/runtime-spec
version: v1.0.0
- package: github.com/fsnotify/fsnotify
version: v1.3.1-1-gf12c6236fe7b5c
- package: github.com/mistifyio/go-zfs
version: v2.1.1-5-g1b4ae6fb4e77b0
- package: github.com/russross/blackfriday
version: v1.4-2-g300106c228d52c
- package: github.com/inconshreveable/mousetrap
version: v1.0
- package: github.com/vishvananda/netlink
version: b2de5d10e38ecce8607e6b438b6d174f389a004e
- package: github.com/mitchellh/go-wordwrap
version: ad45545899c7b13c020ea92b2072220eefad42b8
- package: github.com/peterbourgon/diskv
version: v2.0.1
- package: github.com/Microsoft/go-winio
version: v0.4.5
- package: github.com/cyphar/filepath-securejoin
version: v0.2.1-1-gae69057f2299fb
- package: github.com/Nvveen/Gotty
version: cd527374f1e5bff4938207604a14f2e38a9cf512
- package: github.com/containernetworking/cni
version: v0.6.0
- package: github.com/hashicorp/golang-lru
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
- package: github.com/jonboulle/clockwork
version: 72f9bd7c4e0c2a40055ab3d0f09654f730cce982
- package: github.com/seccomp/libseccomp-golang
version: 1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1
- package: github.com/coreos/pkg
version: v4
- package: github.com/opencontainers/image-spec
version: v1.0.0-rc6-12-g372ad780f63454
- package: github.com/opencontainers/selinux
version: v1.0.0-rc1-5-g4a2974bf1ee960
- package: github.com/cloudflare/cfssl
version: 1.3.2-21-g56268a613adfed
- package: github.com/grpc-ecosystem/go-grpc-prometheus
version: v1.1-4-g2500245aa6110c
- package: github.com/miekg/dns
version: 5d001d020961ae1c184f9f8152fdc73810481677
- package: github.com/godbus/dbus
version: v3
- package: github.com/spf13/pflag
version: v1.0.1
- package: github.com/docker/go-connections
version: v0.3.0
- package: golang.org/x/oauth2
version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4
- package: github.com/gogo/protobuf
version: v0.5
- package: github.com/mxk/go-flowrate
version: cca7078d478f8520f85629ad7c68962d31ed7682
- package: github.com/coreos/go-systemd
version: v17
- package: github.com/opencontainers/go-digest
version: a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
- package: gopkg.in/yaml.v2
version: v2.2.1
- package: github.com/coreos/etcd
version: v3.3.10

View File

@ -0,0 +1,17 @@
package v1
import (
"github.com/rancher/norman/types"
"github.com/rancher/norman/types/factory"
)
var (
APIVersion = types.APIVersion{
Version: "v1",
Group: "k3s.cattle.io",
Path: "/v1-k3s",
}
Schemas = factory.Schemas(&APIVersion).
MustImport(&APIVersion, ListenerConfig{})
)

View File

@ -0,0 +1,16 @@
package v1
import (
"github.com/rancher/norman/pkg/dynamiclistener"
"github.com/rancher/norman/types"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type ListenerConfig struct {
types.Namespaced
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Status dynamiclistener.ListenerStatus `json:"status,omitempty"`
}

View File

@ -0,0 +1,66 @@
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ListenerConfig) DeepCopyInto(out *ListenerConfig) {
*out = *in
out.Namespaced = in.Namespaced
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ListenerConfig.
func (in *ListenerConfig) DeepCopy() *ListenerConfig {
if in == nil {
return nil
}
out := new(ListenerConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ListenerConfig) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ListenerConfigList) DeepCopyInto(out *ListenerConfigList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ListenerConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ListenerConfigList.
func (in *ListenerConfigList) DeepCopy() *ListenerConfigList {
if in == nil {
return nil
}
out := new(ListenerConfigList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ListenerConfigList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@ -0,0 +1,119 @@
package v1
import (
"context"
"sync"
"github.com/rancher/norman/controller"
"github.com/rancher/norman/objectclient"
"github.com/rancher/norman/objectclient/dynamic"
"github.com/rancher/norman/restwatch"
"k8s.io/client-go/rest"
)
type (
contextKeyType struct{}
contextClientsKeyType struct{}
)
type Interface interface {
RESTClient() rest.Interface
controller.Starter
ListenerConfigsGetter
}
type Clients struct {
Interface Interface
ListenerConfig ListenerConfigClient
}
type Client struct {
sync.Mutex
restClient rest.Interface
starters []controller.Starter
listenerConfigControllers map[string]ListenerConfigController
}
func Factory(ctx context.Context, config rest.Config) (context.Context, controller.Starter, error) {
c, err := NewForConfig(config)
if err != nil {
return ctx, nil, err
}
cs := NewClientsFromInterface(c)
ctx = context.WithValue(ctx, contextKeyType{}, c)
ctx = context.WithValue(ctx, contextClientsKeyType{}, cs)
return ctx, c, nil
}
func ClientsFrom(ctx context.Context) *Clients {
return ctx.Value(contextClientsKeyType{}).(*Clients)
}
func From(ctx context.Context) Interface {
return ctx.Value(contextKeyType{}).(Interface)
}
func NewClients(config rest.Config) (*Clients, error) {
iface, err := NewForConfig(config)
if err != nil {
return nil, err
}
return NewClientsFromInterface(iface), nil
}
func NewClientsFromInterface(iface Interface) *Clients {
return &Clients{
Interface: iface,
ListenerConfig: &listenerConfigClient2{
iface: iface.ListenerConfigs(""),
},
}
}
func NewForConfig(config rest.Config) (Interface, error) {
if config.NegotiatedSerializer == nil {
config.NegotiatedSerializer = dynamic.NegotiatedSerializer
}
restClient, err := restwatch.UnversionedRESTClientFor(&config)
if err != nil {
return nil, err
}
return &Client{
restClient: restClient,
listenerConfigControllers: map[string]ListenerConfigController{},
}, nil
}
func (c *Client) RESTClient() rest.Interface {
return c.restClient
}
func (c *Client) Sync(ctx context.Context) error {
return controller.Sync(ctx, c.starters...)
}
func (c *Client) Start(ctx context.Context, threadiness int) error {
return controller.Start(ctx, threadiness, c.starters...)
}
type ListenerConfigsGetter interface {
ListenerConfigs(namespace string) ListenerConfigInterface
}
func (c *Client) ListenerConfigs(namespace string) ListenerConfigInterface {
objectClient := objectclient.NewObjectClient(namespace, c.restClient, &ListenerConfigResource, ListenerConfigGroupVersionKind, listenerConfigFactory{})
return &listenerConfigClient{
ns: namespace,
client: c,
objectClient: objectClient,
}
}

View File

@ -0,0 +1,440 @@
package v1
import (
"context"
"github.com/rancher/norman/controller"
"github.com/rancher/norman/objectclient"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/cache"
)
var (
ListenerConfigGroupVersionKind = schema.GroupVersionKind{
Version: Version,
Group: GroupName,
Kind: "ListenerConfig",
}
ListenerConfigResource = metav1.APIResource{
Name: "listenerconfigs",
SingularName: "listenerconfig",
Namespaced: true,
Kind: ListenerConfigGroupVersionKind.Kind,
}
)
func NewListenerConfig(namespace, name string, obj ListenerConfig) *ListenerConfig {
obj.APIVersion, obj.Kind = ListenerConfigGroupVersionKind.ToAPIVersionAndKind()
obj.Name = name
obj.Namespace = namespace
return &obj
}
type ListenerConfigList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ListenerConfig
}
type ListenerConfigHandlerFunc func(key string, obj *ListenerConfig) (runtime.Object, error)
type ListenerConfigChangeHandlerFunc func(obj *ListenerConfig) (runtime.Object, error)
type ListenerConfigLister interface {
List(namespace string, selector labels.Selector) (ret []*ListenerConfig, err error)
Get(namespace, name string) (*ListenerConfig, error)
}
type ListenerConfigController interface {
Generic() controller.GenericController
Informer() cache.SharedIndexInformer
Lister() ListenerConfigLister
AddHandler(ctx context.Context, name string, handler ListenerConfigHandlerFunc)
AddClusterScopedHandler(ctx context.Context, name, clusterName string, handler ListenerConfigHandlerFunc)
Enqueue(namespace, name string)
Sync(ctx context.Context) error
Start(ctx context.Context, threadiness int) error
}
type ListenerConfigInterface interface {
ObjectClient() *objectclient.ObjectClient
Create(*ListenerConfig) (*ListenerConfig, error)
GetNamespaced(namespace, name string, opts metav1.GetOptions) (*ListenerConfig, error)
Get(name string, opts metav1.GetOptions) (*ListenerConfig, error)
Update(*ListenerConfig) (*ListenerConfig, error)
Delete(name string, options *metav1.DeleteOptions) error
DeleteNamespaced(namespace, name string, options *metav1.DeleteOptions) error
List(opts metav1.ListOptions) (*ListenerConfigList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
DeleteCollection(deleteOpts *metav1.DeleteOptions, listOpts metav1.ListOptions) error
Controller() ListenerConfigController
AddHandler(ctx context.Context, name string, sync ListenerConfigHandlerFunc)
AddLifecycle(ctx context.Context, name string, lifecycle ListenerConfigLifecycle)
AddClusterScopedHandler(ctx context.Context, name, clusterName string, sync ListenerConfigHandlerFunc)
AddClusterScopedLifecycle(ctx context.Context, name, clusterName string, lifecycle ListenerConfigLifecycle)
}
type listenerConfigLister struct {
controller *listenerConfigController
}
func (l *listenerConfigLister) List(namespace string, selector labels.Selector) (ret []*ListenerConfig, err error) {
err = cache.ListAllByNamespace(l.controller.Informer().GetIndexer(), namespace, selector, func(obj interface{}) {
ret = append(ret, obj.(*ListenerConfig))
})
return
}
func (l *listenerConfigLister) Get(namespace, name string) (*ListenerConfig, error) {
var key string
if namespace != "" {
key = namespace + "/" + name
} else {
key = name
}
obj, exists, err := l.controller.Informer().GetIndexer().GetByKey(key)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(schema.GroupResource{
Group: ListenerConfigGroupVersionKind.Group,
Resource: "listenerConfig",
}, key)
}
return obj.(*ListenerConfig), nil
}
type listenerConfigController struct {
controller.GenericController
}
func (c *listenerConfigController) Generic() controller.GenericController {
return c.GenericController
}
func (c *listenerConfigController) Lister() ListenerConfigLister {
return &listenerConfigLister{
controller: c,
}
}
func (c *listenerConfigController) AddHandler(ctx context.Context, name string, handler ListenerConfigHandlerFunc) {
c.GenericController.AddHandler(ctx, name, func(key string, obj interface{}) (interface{}, error) {
if obj == nil {
return handler(key, nil)
} else if v, ok := obj.(*ListenerConfig); ok {
return handler(key, v)
} else {
return nil, nil
}
})
}
func (c *listenerConfigController) AddClusterScopedHandler(ctx context.Context, name, cluster string, handler ListenerConfigHandlerFunc) {
c.GenericController.AddHandler(ctx, name, func(key string, obj interface{}) (interface{}, error) {
if obj == nil {
return handler(key, nil)
} else if v, ok := obj.(*ListenerConfig); ok && controller.ObjectInCluster(cluster, obj) {
return handler(key, v)
} else {
return nil, nil
}
})
}
type listenerConfigFactory struct {
}
func (c listenerConfigFactory) Object() runtime.Object {
return &ListenerConfig{}
}
func (c listenerConfigFactory) List() runtime.Object {
return &ListenerConfigList{}
}
func (s *listenerConfigClient) Controller() ListenerConfigController {
s.client.Lock()
defer s.client.Unlock()
c, ok := s.client.listenerConfigControllers[s.ns]
if ok {
return c
}
genericController := controller.NewGenericController(ListenerConfigGroupVersionKind.Kind+"Controller",
s.objectClient)
c = &listenerConfigController{
GenericController: genericController,
}
s.client.listenerConfigControllers[s.ns] = c
s.client.starters = append(s.client.starters, c)
return c
}
type listenerConfigClient struct {
client *Client
ns string
objectClient *objectclient.ObjectClient
controller ListenerConfigController
}
func (s *listenerConfigClient) ObjectClient() *objectclient.ObjectClient {
return s.objectClient
}
func (s *listenerConfigClient) Create(o *ListenerConfig) (*ListenerConfig, error) {
obj, err := s.objectClient.Create(o)
return obj.(*ListenerConfig), err
}
func (s *listenerConfigClient) Get(name string, opts metav1.GetOptions) (*ListenerConfig, error) {
obj, err := s.objectClient.Get(name, opts)
return obj.(*ListenerConfig), err
}
func (s *listenerConfigClient) GetNamespaced(namespace, name string, opts metav1.GetOptions) (*ListenerConfig, error) {
obj, err := s.objectClient.GetNamespaced(namespace, name, opts)
return obj.(*ListenerConfig), err
}
func (s *listenerConfigClient) Update(o *ListenerConfig) (*ListenerConfig, error) {
obj, err := s.objectClient.Update(o.Name, o)
return obj.(*ListenerConfig), err
}
func (s *listenerConfigClient) Delete(name string, options *metav1.DeleteOptions) error {
return s.objectClient.Delete(name, options)
}
func (s *listenerConfigClient) DeleteNamespaced(namespace, name string, options *metav1.DeleteOptions) error {
return s.objectClient.DeleteNamespaced(namespace, name, options)
}
func (s *listenerConfigClient) List(opts metav1.ListOptions) (*ListenerConfigList, error) {
obj, err := s.objectClient.List(opts)
return obj.(*ListenerConfigList), err
}
func (s *listenerConfigClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return s.objectClient.Watch(opts)
}
// Patch applies the patch and returns the patched deployment.
func (s *listenerConfigClient) Patch(o *ListenerConfig, patchType types.PatchType, data []byte, subresources ...string) (*ListenerConfig, error) {
obj, err := s.objectClient.Patch(o.Name, o, patchType, data, subresources...)
return obj.(*ListenerConfig), err
}
func (s *listenerConfigClient) DeleteCollection(deleteOpts *metav1.DeleteOptions, listOpts metav1.ListOptions) error {
return s.objectClient.DeleteCollection(deleteOpts, listOpts)
}
func (s *listenerConfigClient) AddHandler(ctx context.Context, name string, sync ListenerConfigHandlerFunc) {
s.Controller().AddHandler(ctx, name, sync)
}
func (s *listenerConfigClient) AddLifecycle(ctx context.Context, name string, lifecycle ListenerConfigLifecycle) {
sync := NewListenerConfigLifecycleAdapter(name, false, s, lifecycle)
s.Controller().AddHandler(ctx, name, sync)
}
func (s *listenerConfigClient) AddClusterScopedHandler(ctx context.Context, name, clusterName string, sync ListenerConfigHandlerFunc) {
s.Controller().AddClusterScopedHandler(ctx, name, clusterName, sync)
}
func (s *listenerConfigClient) AddClusterScopedLifecycle(ctx context.Context, name, clusterName string, lifecycle ListenerConfigLifecycle) {
sync := NewListenerConfigLifecycleAdapter(name+"_"+clusterName, true, s, lifecycle)
s.Controller().AddClusterScopedHandler(ctx, name, clusterName, sync)
}
type ListenerConfigIndexer func(obj *ListenerConfig) ([]string, error)
type ListenerConfigClientCache interface {
Get(namespace, name string) (*ListenerConfig, error)
List(namespace string, selector labels.Selector) ([]*ListenerConfig, error)
Index(name string, indexer ListenerConfigIndexer)
GetIndexed(name, key string) ([]*ListenerConfig, error)
}
type ListenerConfigClient interface {
Create(*ListenerConfig) (*ListenerConfig, error)
Get(namespace, name string, opts metav1.GetOptions) (*ListenerConfig, error)
Update(*ListenerConfig) (*ListenerConfig, error)
Delete(namespace, name string, options *metav1.DeleteOptions) error
List(namespace string, opts metav1.ListOptions) (*ListenerConfigList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
Cache() ListenerConfigClientCache
OnCreate(ctx context.Context, name string, sync ListenerConfigChangeHandlerFunc)
OnChange(ctx context.Context, name string, sync ListenerConfigChangeHandlerFunc)
OnRemove(ctx context.Context, name string, sync ListenerConfigChangeHandlerFunc)
Enqueue(namespace, name string)
Generic() controller.GenericController
ObjectClient() *objectclient.ObjectClient
Interface() ListenerConfigInterface
}
type listenerConfigClientCache struct {
client *listenerConfigClient2
}
type listenerConfigClient2 struct {
iface ListenerConfigInterface
controller ListenerConfigController
}
func (n *listenerConfigClient2) Interface() ListenerConfigInterface {
return n.iface
}
func (n *listenerConfigClient2) Generic() controller.GenericController {
return n.iface.Controller().Generic()
}
func (n *listenerConfigClient2) ObjectClient() *objectclient.ObjectClient {
return n.Interface().ObjectClient()
}
func (n *listenerConfigClient2) Enqueue(namespace, name string) {
n.iface.Controller().Enqueue(namespace, name)
}
func (n *listenerConfigClient2) Create(obj *ListenerConfig) (*ListenerConfig, error) {
return n.iface.Create(obj)
}
func (n *listenerConfigClient2) Get(namespace, name string, opts metav1.GetOptions) (*ListenerConfig, error) {
return n.iface.GetNamespaced(namespace, name, opts)
}
func (n *listenerConfigClient2) Update(obj *ListenerConfig) (*ListenerConfig, error) {
return n.iface.Update(obj)
}
func (n *listenerConfigClient2) Delete(namespace, name string, options *metav1.DeleteOptions) error {
return n.iface.DeleteNamespaced(namespace, name, options)
}
func (n *listenerConfigClient2) List(namespace string, opts metav1.ListOptions) (*ListenerConfigList, error) {
return n.iface.List(opts)
}
func (n *listenerConfigClient2) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return n.iface.Watch(opts)
}
func (n *listenerConfigClientCache) Get(namespace, name string) (*ListenerConfig, error) {
return n.client.controller.Lister().Get(namespace, name)
}
func (n *listenerConfigClientCache) List(namespace string, selector labels.Selector) ([]*ListenerConfig, error) {
return n.client.controller.Lister().List(namespace, selector)
}
func (n *listenerConfigClient2) Cache() ListenerConfigClientCache {
n.loadController()
return &listenerConfigClientCache{
client: n,
}
}
func (n *listenerConfigClient2) OnCreate(ctx context.Context, name string, sync ListenerConfigChangeHandlerFunc) {
n.loadController()
n.iface.AddLifecycle(ctx, name+"-create", &listenerConfigLifecycleDelegate{create: sync})
}
func (n *listenerConfigClient2) OnChange(ctx context.Context, name string, sync ListenerConfigChangeHandlerFunc) {
n.loadController()
n.iface.AddLifecycle(ctx, name+"-change", &listenerConfigLifecycleDelegate{update: sync})
}
func (n *listenerConfigClient2) OnRemove(ctx context.Context, name string, sync ListenerConfigChangeHandlerFunc) {
n.loadController()
n.iface.AddLifecycle(ctx, name, &listenerConfigLifecycleDelegate{remove: sync})
}
func (n *listenerConfigClientCache) Index(name string, indexer ListenerConfigIndexer) {
err := n.client.controller.Informer().GetIndexer().AddIndexers(map[string]cache.IndexFunc{
name: func(obj interface{}) ([]string, error) {
if v, ok := obj.(*ListenerConfig); ok {
return indexer(v)
}
return nil, nil
},
})
if err != nil {
panic(err)
}
}
func (n *listenerConfigClientCache) GetIndexed(name, key string) ([]*ListenerConfig, error) {
var result []*ListenerConfig
objs, err := n.client.controller.Informer().GetIndexer().ByIndex(name, key)
if err != nil {
return nil, err
}
for _, obj := range objs {
if v, ok := obj.(*ListenerConfig); ok {
result = append(result, v)
}
}
return result, nil
}
func (n *listenerConfigClient2) loadController() {
if n.controller == nil {
n.controller = n.iface.Controller()
}
}
type listenerConfigLifecycleDelegate struct {
create ListenerConfigChangeHandlerFunc
update ListenerConfigChangeHandlerFunc
remove ListenerConfigChangeHandlerFunc
}
func (n *listenerConfigLifecycleDelegate) HasCreate() bool {
return n.create != nil
}
func (n *listenerConfigLifecycleDelegate) Create(obj *ListenerConfig) (runtime.Object, error) {
if n.create == nil {
return obj, nil
}
return n.create(obj)
}
func (n *listenerConfigLifecycleDelegate) HasFinalize() bool {
return n.remove != nil
}
func (n *listenerConfigLifecycleDelegate) Remove(obj *ListenerConfig) (runtime.Object, error) {
if n.remove == nil {
return obj, nil
}
return n.remove(obj)
}
func (n *listenerConfigLifecycleDelegate) Updated(obj *ListenerConfig) (runtime.Object, error) {
if n.update == nil {
return obj, nil
}
return n.update(obj)
}

View File

@ -0,0 +1,62 @@
package v1
import (
"github.com/rancher/norman/lifecycle"
"k8s.io/apimachinery/pkg/runtime"
)
type ListenerConfigLifecycle interface {
Create(obj *ListenerConfig) (runtime.Object, error)
Remove(obj *ListenerConfig) (runtime.Object, error)
Updated(obj *ListenerConfig) (runtime.Object, error)
}
type listenerConfigLifecycleAdapter struct {
lifecycle ListenerConfigLifecycle
}
func (w *listenerConfigLifecycleAdapter) HasCreate() bool {
o, ok := w.lifecycle.(lifecycle.ObjectLifecycleCondition)
return !ok || o.HasCreate()
}
func (w *listenerConfigLifecycleAdapter) HasFinalize() bool {
o, ok := w.lifecycle.(lifecycle.ObjectLifecycleCondition)
return !ok || o.HasFinalize()
}
func (w *listenerConfigLifecycleAdapter) Create(obj runtime.Object) (runtime.Object, error) {
o, err := w.lifecycle.Create(obj.(*ListenerConfig))
if o == nil {
return nil, err
}
return o, err
}
func (w *listenerConfigLifecycleAdapter) Finalize(obj runtime.Object) (runtime.Object, error) {
o, err := w.lifecycle.Remove(obj.(*ListenerConfig))
if o == nil {
return nil, err
}
return o, err
}
func (w *listenerConfigLifecycleAdapter) Updated(obj runtime.Object) (runtime.Object, error) {
o, err := w.lifecycle.Updated(obj.(*ListenerConfig))
if o == nil {
return nil, err
}
return o, err
}
func NewListenerConfigLifecycleAdapter(name string, clusterScoped bool, client ListenerConfigInterface, l ListenerConfigLifecycle) ListenerConfigHandlerFunc {
adapter := &listenerConfigLifecycleAdapter{lifecycle: l}
syncFn := lifecycle.NewObjectLifecycleAdapter(name, clusterScoped, adapter, client.ObjectClient())
return func(key string, obj *ListenerConfig) (runtime.Object, error) {
newObj, err := syncFn(key, obj)
if o, ok := newObj.(runtime.Object); ok {
return o, err
}
return nil, err
}
}

View File

@ -0,0 +1,40 @@
package v1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
GroupName = "k3s.cattle.io"
Version = "v1"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: Version}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
// TODO this gets cleaned up when the types are fixed
scheme.AddKnownTypes(SchemeGroupVersion,
&ListenerConfig{},
&ListenerConfigList{},
)
return nil
}

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/rancher/norman/generator/cleanup"
"github.com/sirupsen/logrus"
)
func main() {
if err := cleanup.Cleanup("./types"); err != nil {
logrus.Fatal(err)
}
}

17
types/codegen/main.go Normal file
View File

@ -0,0 +1,17 @@
package main
import (
"github.com/rancher/norman/generator"
v1 "github.com/rancher/rio/types/apis/k3s.cattle.io/v1"
"github.com/sirupsen/logrus"
)
var (
basePackage = "github.com/rancher/rio/types"
)
func main() {
if err := generator.DefaultGenerate(v1.Schemas, basePackage, false, nil); err != nil {
logrus.Fatal(err)
}
}

17
vendor.conf Normal file
View File

@ -0,0 +1,17 @@
package=github.com/rancher/rio
package=github.com/jteeuwen/go-bindata
package=github.com/jteeuwen/go-bindata/go-bindata
k8s.io/kubernetes a1d7d1b140f43b6503311ffc1dd80017c553bf8e file:///home/darren/src/kuberlite/.git transitive=true,staging=true
github.com/rancher/norman 5726ebfba191eef161af9638e85bf4fcaa58e008 https://github.com/ibuildthecloud/norman.git
github.com/coreos/flannel 39af3d7e46f2efa156644e247bdcf3b5bc5f1394
github.com/natefinch/lumberjack aee4629129445bbdfb69aa565537dcfa16544311
github.com/gorilla/mux v1.6.2
github.com/gorilla/websocket v1.2.0
github.com/mattn/go-sqlite3 v1.9.0
github.com/patrickmn/go-cache v2.1.0
golang.org/x/crypto a49355c7e3f8fe157a85be2f77e6e269a0f89602
gopkg.in/freddierice/go-losetup.v1 fc9adea44124401d8bfef3a97eaf61b5d44cc2c6
github.com/urfave/cli 8e01ec4cd3e2d84ab2fe90d8210528ffbb06d8ff
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c

6
version/version.go Normal file
View File

@ -0,0 +1,6 @@
package version
var (
Version = "dev"
GitCommit = "HEAD"
)