Adding initial codebase

This commit is contained in:
pprokop 2016-07-18 18:57:12 +02:00
parent 9bad9a4cee
commit e43a271dbf
16 changed files with 699 additions and 0 deletions

95
dependencies/config/config.go vendored Normal file
View File

@ -0,0 +1,95 @@
package config
import (
"fmt"
"net"
"os"
"path/filepath"
"strings"
"text/template"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/logger"
"github.com/stackanetes/docker-entrypoint/util/env"
)
type Config struct {
name string
params struct {
iface string
HOSTNAME string
IP string
IP_ERLANG string
}
}
func init() {
configEnv := fmt.Sprintf("%sCONFIG", entry.DependencyPrefix)
if configDeps := env.SplitEnvToList(configEnv); len(configDeps) > 0 {
for _, dep := range configDeps {
entry.Register(NewConfig(dep))
}
}
}
func NewConfig(name string) Config {
var config Config
config.name = name
iface := os.Getenv("INTERFACE_NAME")
if iface == "" {
logger.Error.Print("Environment variable INTERFACE_NAME not set")
os.Exit(1)
}
hostname := os.Getenv("HOSTNAME")
if hostname == "" {
logger.Error.Print("Environment variable HOSTNAME not set")
}
config.params.HOSTNAME = hostname
config.params.iface = iface
i, err := net.InterfaceByName(iface)
if err != nil {
logger.Error.Printf("Cannot get iface: %v", err)
os.Exit(1)
}
address, err := i.Addrs()
if err != nil || len(address) == 0 {
logger.Error.Printf("Cannot get ip: %v", err)
os.Exit(1)
}
config.params.IP = strings.Split(address[0].String(), "/")[0]
config.params.IP_ERLANG = strings.Replace(config.params.IP, ".", ",", -1)
return config
}
func (c Config) IsResolved(entrypoint *entry.Entrypoint) (bool, error) {
logger.Info.Print(c.GetName())
err := CreateDirectory(c.GetName())
if err != nil {
return false, fmt.Errorf("Couldn't create directory: %v", err)
}
config, err := os.Create(c.GetName())
if err != nil {
return false, fmt.Errorf("Couldn't touch file %v: %v", c.GetName(), err)
}
file := filepath.Base(c.GetName())
temp := template.Must(template.New(file).ParseFiles(fmt.Sprintf("/configmaps/%s/%s", file, file)))
if err = temp.Execute(config, c.params); err != nil {
return false, err
}
return true, nil
}
func (c Config) GetName() string {
return c.name
}
func CreateDirectory(file string) error {
err := os.MkdirAll(filepath.Dir(file), 0644)
if err != nil {
return err
}
return nil
}

48
dependencies/container/container.go vendored Normal file
View File

@ -0,0 +1,48 @@
package container
import (
"fmt"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/util/env"
"os"
)
type Container struct {
name string
}
func init() {
containerEnv := fmt.Sprintf("%sCONTAINER", entry.DependencyPrefix)
if containerDeps := env.SplitEnvToList(containerEnv); len(containerDeps) > 0 {
for _, dep := range containerDeps {
entry.Register(NewContainer(dep))
}
}
}
func NewContainer(name string) Container {
return Container{name: name}
}
func (c Container) IsResolved(entrypoint *entry.Entrypoint) (bool, error) {
myPodName := os.Getenv("POD_NAME")
if myPodName == "" {
return false, fmt.Errorf("Environment variable POD_NAME not set")
}
pod, err := entrypoint.Client.Pods(entrypoint.Namespace).Get(myPodName)
if err != nil {
return false, err
}
containers := pod.Status.ContainerStatuses
for _, container := range containers {
if container.Name == c.GetName() && container.State.Running != nil {
return true, nil
}
}
return false, nil
}
func (c Container) GetName() string {
return c.name
}

87
dependencies/daemonset/daemonset.go vendored Normal file
View File

@ -0,0 +1,87 @@
package daemonset
import (
"fmt"
"os"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/logger"
"github.com/stackanetes/docker-entrypoint/util/env"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/labels"
)
type Daemonset struct {
name string
}
func init() {
daemonsetEnv := fmt.Sprintf("%sDAEMONSET", entry.DependencyPrefix)
if daemonsetsDeps := env.SplitEnvToList(daemonsetEnv); daemonsetsDeps != nil {
for _, dep := range daemonsetsDeps {
entry.Register(NewDaemonset(dep))
}
}
}
func NewDaemonset(name string) Daemonset {
return Daemonset{name: name}
}
func (d Daemonset) IsResolved(entrypoint *entry.Entrypoint) (bool, error) {
daemonset, err := entrypoint.Client.ExtensionsClient.DaemonSets(entrypoint.Namespace).Get(d.name)
if err != nil {
return false, err
}
label := labels.SelectorFromSet(daemonset.Spec.Selector.MatchLabels)
opts := api.ListOptions{LabelSelector: label}
pods, err := entrypoint.Client.Pods(entrypoint.Namespace).List(opts)
if err != nil {
return false, err
}
myPodName := os.Getenv("POD_NAME")
if myPodName == "" {
logger.Error.Print("Environment variable POD_NAME not set")
os.Exit(1)
}
myPod, err := entrypoint.Client.Pods(entrypoint.Namespace).Get(myPodName)
if err != nil {
logger.Error.Printf("Getting POD: %v failed : %v", myPodName, err)
os.Exit(1)
}
myHost := myPod.Status.HostIP
for _, pod := range pods.Items {
if !podReady(&pod) {
return false, fmt.Errorf("Pod %v of daemonset %v is not ready", pod.Name, d.GetName())
}
}
if !isPodOnHost(pods.Items, myHost) {
return false, fmt.Errorf("Hostname mismatch: Daemonset %v is not on the same host as Pod %v", d.GetName(), myPodName)
}
return true, nil
}
func (d Daemonset) GetName() string {
return d.name
}
func podReady(pod *api.Pod) bool {
for _, cond := range pod.Status.Conditions {
if cond.Type == api.PodReady && cond.Status == api.ConditionTrue {
return true
}
}
return false
}
func isPodOnHost(podList []api.Pod, hostIP string) bool {
for _, pod := range podList {
if pod.Status.HostIP == hostIP {
return true
}
}
return false
}

75
dependencies/entrypoint.go vendored Normal file
View File

@ -0,0 +1,75 @@
package entrypoint
import (
"github.com/stackanetes/docker-entrypoint/logger"
// "k8s.io/kubernetes/pkg/client/restclient"
"fmt"
cl "k8s.io/kubernetes/pkg/client/unversioned"
"os"
"sync"
"time"
)
var dependencies []Resolver // List containing all dependencies to be resolved
const (
DependencyPrefix = "DEPENDENCY_" //Prefix for env variables
interval = 2
)
// Object containing k8s client
type Entrypoint struct {
Client *cl.Client
Namespace string
}
//Constructor for entrypoint
func NewEntrypoint(client *cl.Client) (entry *Entrypoint, err error) {
entry = new(Entrypoint)
if entry.Client = client; client == nil {
if entry.Client, err = cl.NewInCluster(); err != nil {
err = fmt.Errorf("Error while creating k8s client: %s", err)
return entry, err
}
}
if entry.Namespace = os.Getenv("NAMESPACE"); entry.Namespace == "" {
logger.Warning.Print("NAMESPACE env not set, using default")
entry.Namespace = "default"
}
return entry, err
}
func (e *Entrypoint) Resolve() {
var wg sync.WaitGroup
for _, dep := range dependencies {
wg.Add(1)
go func(dep Resolver) {
defer wg.Done()
logger.Info.Printf("Resolving %s", dep.GetName())
var err error
status := false
for status == false {
if status, err = dep.IsResolved(e); err != nil {
logger.Warning.Printf("Resolving dependency for %v failed: %v", dep.GetName(), err)
}
time.Sleep(interval * time.Second)
}
logger.Info.Printf("Dependency %v is resolved", dep.GetName())
}(dep)
}
wg.Wait()
}
type Resolver interface {
// GetType() string
IsResolved(entrypoint *Entrypoint) (bool, error)
GetName() string
}
func Register(res Resolver) {
if res == nil {
panic("Entrypoint: could not register nil Resolver")
}
dependencies = append(dependencies, res)
}

17
dependencies/entrypoint_test.go vendored Normal file
View File

@ -0,0 +1,17 @@
package entrypoint
import "testing"
type dummyResolver struct {
}
func (d *dummyResolver) IsResolved(name string) (bool, error) {
return true, nil
}
func TestRegisterNewDependency(t *testing.T) {
dummy := new(dummyResolver)
Register(dummy)
if len(Dependencies) != 1 {
t.Errorf("Expecting dependencies len to be 1 got %v", len(Dependencies))
}
}

41
dependencies/job/job.go vendored Normal file
View File

@ -0,0 +1,41 @@
package job
import (
"fmt"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/util/env"
)
type Job struct {
name string
}
func init() {
jobsEnv := fmt.Sprintf("%sJOBS", entry.DependencyPrefix)
if jobsDeps := env.SplitEnvToList(jobsEnv); len(jobsDeps) > 0 {
for _, dep := range jobsDeps {
entry.Register(NewJob(dep))
}
}
}
func NewJob(name string) Job {
return Job{name: name}
}
func (j Job) IsResolved(entrypoint *entry.Entrypoint) (bool, error) {
job, err := entrypoint.Client.ExtensionsClient.Jobs(entrypoint.Namespace).Get(j.name)
if err != nil {
return false, err
}
if job.Status.Succeeded == 0 {
return false, fmt.Errorf("Job %v is not completed yet", j.GetName())
}
return true, nil
}
func (j Job) GetName() string {
return j.name
}

25
dependencies/job/job_test.go vendored Normal file
View File

@ -0,0 +1,25 @@
package job
import (
"fmt"
"os"
"testing"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/logger"
)
func init() {
os.Setenv(fmt.Sprintf("%sJOBS", entry.DependencyPrefix), "test")
}
func TestRegisterNewJob(t *testing.T) {
logger.Info.Printf("%v", os.Getenv(fmt.Sprintf("%sJOBS", entry.DependencyPrefix)))
if len(entry.Dependencies) != 1 {
t.Errorf("Expecting len of dependencies to be 1 not %v", len(entry.Dependencies))
}
if entry.Dependencies[0].GetName() != "test" {
t.Errorf("Expecting name to be test not %s", entry.Dependencies[0].GetName())
}
}

40
dependencies/service/service.go vendored Normal file
View File

@ -0,0 +1,40 @@
package service
import (
"fmt"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/util/env"
)
type Service struct {
name string
}
func init() {
serviceEnv := fmt.Sprintf("%sSERVICE", entry.DependencyPrefix)
if serviceDeps := env.SplitEnvToList(serviceEnv); len(serviceDeps) > 0 {
for _, dep := range serviceDeps {
entry.Register(NewService(dep))
}
}
}
func NewService(name string) Service {
return Service{name: name}
}
func (s Service) IsResolved(entrypoint *entry.Entrypoint) (bool, error) {
e, err := entrypoint.Client.Endpoints(entrypoint.Namespace).Get(s.GetName())
if err != nil {
return false, err
}
if len(e.Subsets) > 0 {
return true, nil
}
return false, fmt.Errorf("Service %v has no endpoints", s.GetName())
}
func (s Service) GetName() string {
return s.name
}

25
dependencies/service/service_test.go vendored Normal file
View File

@ -0,0 +1,25 @@
package service
import (
"fmt"
"os"
"testing"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/logger"
)
func init() {
os.Setenv(fmt.Sprintf("%sSERVICE", entry.DependencyPrefix), "test")
}
func TestRegisterNewService(t *testing.T) {
logger.Info.Printf("%v", os.Getenv(fmt.Sprintf("%sSERVICE", entry.DependencyPrefix)))
if len(entry.Dependencies) != 1 {
t.Errorf("Expecting len of dependencies to be 1 not %v", len(entry.Dependencies))
}
if entry.Dependencies[0].GetName() != "test" {
t.Errorf("Expecting name to be test not %s", entry.Dependencies[0].GetName())
}
}

43
dependencies/socket/socket.go vendored Normal file
View File

@ -0,0 +1,43 @@
package socket
import (
"fmt"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/util/env"
"os"
)
type Socket struct {
name string
}
func init() {
socketEnv := fmt.Sprintf("%sSOCKET", entry.DependencyPrefix)
if socketDeps := env.SplitEnvToList(socketEnv); len(socketDeps) > 0 {
for _, dep := range socketDeps {
entry.Register(NewSocket(dep))
}
}
}
func NewSocket(name string) Socket {
return Socket{name: name}
}
func (s Socket) GetName() string {
return s.name
}
func (s Socket) IsResolved(entrypoint *entry.Entrypoint) (bool, error) {
_, err := os.Stat(s.GetName())
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, fmt.Errorf("Socket %v doesn't exists", s.GetName())
}
if os.IsPermission(err) {
return false, fmt.Errorf("I have no permission to %v", s.GetName())
}
return false, err
}

40
kubernetes-entrypoint.go Normal file
View File

@ -0,0 +1,40 @@
package main
import (
"os"
entry "github.com/stackanetes/docker-entrypoint/dependencies"
"github.com/stackanetes/docker-entrypoint/logger"
comm "github.com/stackanetes/docker-entrypoint/util/command"
"github.com/stackanetes/docker-entrypoint/util/env"
cl "k8s.io/kubernetes/pkg/client/unversioned"
//Register resolvers
_ "github.com/stackanetes/docker-entrypoint/dependencies/config"
_ "github.com/stackanetes/docker-entrypoint/dependencies/container"
_ "github.com/stackanetes/docker-entrypoint/dependencies/daemonset"
_ "github.com/stackanetes/docker-entrypoint/dependencies/job"
_ "github.com/stackanetes/docker-entrypoint/dependencies/service"
_ "github.com/stackanetes/docker-entrypoint/dependencies/socket"
)
func main() {
var client *cl.Client
var command []string
var entrypoint *entry.Entrypoint
var err error
if entrypoint, err = entry.NewEntrypoint(client); err != nil {
logger.Error.Printf("Creating entrypoint failed: %v", err)
os.Exit(1)
}
entrypoint.Resolve()
if command = env.SplitEnvToList("COMMAND", " "); len(command) == 0 {
logger.Error.Printf("COMMAND env is empty")
os.Exit(1)
}
if err = comm.ExecuteCommand(command); err != nil {
logger.Error.Printf("Executing command failed: %v", err)
}
}

21
logger/logger.go Normal file
View File

@ -0,0 +1,21 @@
package logger
import (
"log"
"os"
)
var (
//"Info logger""
Info *log.Logger
//"Error logger"
Error *log.Logger
//Warning logger
Warning *log.Logger
)
func init() {
Info = log.New(os.Stdout, "Entrypoint INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
Warning = log.New(os.Stdout, "Entrypoint WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
Error = log.New(os.Stderr, "Entrypoint Error: ", log.Ldate|log.Ltime|log.Lshortfile)
}

26
util/command/command.go Normal file
View File

@ -0,0 +1,26 @@
package command
import (
"os"
"os/exec"
)
func ExecuteCommand(command []string) error {
path, err := exec.LookPath(command[0])
if err != nil {
return err
}
cmd := exec.Cmd{
Path: path,
Args: command,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
err = cmd.Run()
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,19 @@
package command
import "testing"
func TestExecuteCommandSuccess(t *testing.T) {
successCommand := []string{"echo", "test"}
err := ExecuteCommand(successCommand)
if err != nil {
t.Errorf("Expecting: command to success not %v", err)
}
}
func TestExecuteCommandFail(t *testing.T) {
errorCommand := []string{"false"}
err := ExecuteCommand(errorCommand)
if err == nil {
t.Errorf("Expecting command to fail")
}
}

20
util/env/env.go vendored Normal file
View File

@ -0,0 +1,20 @@
package env
import (
"os"
"strings"
)
func SplitEnvToList(env string, s ...string) (envList []string) {
separator := ","
if len(s) > 0 {
separator = s[0]
}
e := os.Getenv(env)
if e == "" {
return envList
}
envList = strings.Split(e, separator)
return envList
}

77
util/env/env_test.go vendored Normal file
View File

@ -0,0 +1,77 @@
package env
import (
"os"
"testing"
)
func TestSplitEnvToListWithColon(t *testing.T) {
os.Setenv("TEST_LIST", "foo,bar")
list := SplitEnvToList("TEST_LIST")
if list == nil {
t.Errorf("Expected: not nil")
}
if list[0] != "foo" {
t.Errorf("Expected: foo got %s", list[0])
}
if list[1] != "bar" {
t.Errorf("Expected: bar got %s", list[1])
}
os.Setenv("TEST_LIST", "foo1")
list1 := SplitEnvToList("TEST_LIST")
if list1 == nil {
t.Errorf("Expected: not nil")
}
if len(list1) != 1 {
t.Errorf("Expected len to be 1 not %i", len(list1))
}
if list1[0] != "foo1" {
t.Errorf("Expected: foo1 got %s", list1[0])
}
}
func TestSplitEnvToListWithSpace(t *testing.T) {
os.Setenv("TEST_LIST", "foo bar")
list := SplitEnvToList("TEST_LIST", " ")
if list == nil {
t.Errorf("Expected: not nil")
}
if list[0] != "foo" {
t.Errorf("Expected: foo got %s", list[0])
}
if list[1] != "bar" {
t.Errorf("Expected: bar got %s", list[1])
}
os.Setenv("TEST_LIST", "foo1")
list1 := SplitEnvToList("TEST_LIST", " ")
if list1 == nil {
t.Errorf("Expected: not nil")
}
if len(list1) != 1 {
t.Errorf("Expected len to be 1 not %i", len(list1))
}
if list1[0] != "foo1" {
t.Errorf("Expected: foo1 got %s", list1[0])
}
}
func TestSplitEmptyEnvWithColon(t *testing.T) {
os.Setenv("TEST_LIST", "")
list := SplitEnvToList("TEST_LIST")
if list != nil {
t.Errorf("Expected nil got %v", list)
}
}
func TestSplitEmptyEnvWithSpace(t *testing.T) {
os.Setenv("TEST_LIST", "")
list := SplitEnvToList("TEST_LIST", " ")
if list != nil {
t.Errorf("Expected nil got %v", list)
}
}