<commit_before>package notetxt

import (
        "regexp"
        "strings"
        "os"
//        "fmt"
        "bufio"
        "errors"
        "path"
        "path/filepath"
        "io/ioutil"
)
var title_clearer = regexp.MustCompile("[^a-zA-Z0-9\\s\\.\\-_]+")
var whitespace_clearer = regexp.MustCompile("\\s+")

func TitleToFilename (title string) string {

        // strip all non-conforming characters
        out := title_clearer.ReplaceAllString(title, "")

        // title shall be lowercase
        out = strings.ToLower(out)

        // every whitespace should become a space
        // if there are multiple whitespace insert only one space as a result
        out = whitespace_clearer.ReplaceAllString(out, " ")

        // every white space should become a dash (because they look nice)
        out = strings.Replace(out, " ", "-", -1)
        return out
}

var filename_regex = regexp.MustCompile("^[a-zA-Z0-9\\-\\.]+$")

func FilenameMatches(filename string) bool {
        return filename_regex.MatchString(filename)
}

type Note struct {
        Name string
        Filename string
        Tags []string
}

func readFilesInDir(dir string, subdir string) ([]string, []string) {
        var symlinks []string
        var files []string
        contents, _ := ioutil.ReadDir(dir + "/" + subdir)
        for _, f := range contents {
                if f.IsDir() {
                        t_files, t_syms := readFilesInDir(dir, subdir + "/" + f.Name())
                        files = append(files,t_files...)
                        symlinks = append(symlinks, t_syms...)
                } else {
                        if f.Mode() & os.ModeSymlink != 0 {
                                symlinks = append(symlinks, dir + subdir + "/" + f.Name())
                        } else {
                                files = append(files, dir + subdir + "/" + f.Name())
                        }
                }
        }
        return files, symlinks
}

func findTags(filename string, notedir string, symlinks []string) []string {
        var out []string

        plain_tag := strings.Replace(path.Dir(filename), notedir, "", 1)
        if len(plain_tag) != 0 {
                out = append(out, plain_tag)
        }

        for _, f := range symlinks {
                p, err := filepath.EvalSymlinks(f)
                if err != nil {
                        panic(err);
                }

                if p == filename {
                        out = append(out, strings.Replace(path.Dir(f), notedir, "", 1))
                }
        }

        return out
}

func ParseNote(notedir string, filename string, symlinks []string) (Note, error) {
        var note = Note{}
        note.Filename = filename

        f, err := os.Open(filename)
        if err != nil {
                return note, err
        }

        defer f.Close()
        reader := bufio.NewReaderSize(f, 4*1024)

        line, prefix, err := reader.ReadLine()
        if err != nil {
                return note, err
        }

        if prefix {
                return note, errors.New("Buffer reader too small for the name of the note.")
        }

        note.Name = string(line)

        note.Tags = findTags(filename, notedir, symlinks)

        return note, nil
}

func ParseDir(notedir string) ([]Note, error) {
        var notes []Note

        notedir, _ = filepath.Abs(notedir)
        files, symlinks := readFilesInDir(notedir, "")

        for _, f := range files {
                note, err := ParseNote(notedir, f, symlinks)
                if err != nil {
                        return nil, err
                }

                notes = append(notes, note)
        }

        return notes, nil
}

<commit_msg>update: Added CreateNote function<commit_after>package notetxt

import (
        "regexp"
        "strings"
        "os"
        "fmt"
        "bufio"
        "errors"
        "path"
        "path/filepath"
        "io/ioutil"
)
var title_clearer = regexp.MustCompile("[^a-zA-Z0-9\\s\\.\\-_]+")
var whitespace_clearer = regexp.MustCompile("\\s+")

func TitleToFilename (title string) string {

        // strip all non-conforming characters
        out := title_clearer.ReplaceAllString(title, "")

        // title shall be lowercase
        out = strings.ToLower(out)

        // every whitespace should become a space
        // if there are multiple whitespace insert only one space as a result
        out = whitespace_clearer.ReplaceAllString(out, " ")

        // every white space should become a dash (because they look nice)
        out = strings.Replace(out, " ", "-", -1)
        return out
}

var filename_regex = regexp.MustCompile("^[a-zA-Z0-9\\-\\.]+$")

func FilenameMatches(filename string) bool {
        return filename_regex.MatchString(filename)
}

type Note struct {
        Name string
        Filename string
        Tags []string
}

func readFilesInDir(dir string, subdir string) ([]string, []string) {
        var symlinks []string
        var files []string
        contents, _ := ioutil.ReadDir(dir + "/" + subdir)
        for _, f := range contents {
                if f.IsDir() {
                        t_files, t_syms := readFilesInDir(dir, subdir + "/" + f.Name())
                        files = append(files,t_files...)
                        symlinks = append(symlinks, t_syms...)
                } else {
                        if f.Mode() & os.ModeSymlink != 0 {
                                symlinks = append(symlinks, dir + subdir + "/" + f.Name())
                        } else {
                                files = append(files, dir + subdir + "/" + f.Name())
                        }
                }
        }
        return files, symlinks
}

func findTags(filename string, notedir string, symlinks []string) []string {
        var out []string

        plain_tag := strings.Replace(path.Dir(filename), notedir, "", 1)
        if len(plain_tag) != 0 {
                out = append(out, plain_tag)
        }

        for _, f := range symlinks {
                p, err := filepath.EvalSymlinks(f)
                if err != nil {
                        panic(err);
                }

                if p == filename {
                        out = append(out, strings.Replace(path.Dir(f), notedir, "", 1))
                }
        }

        return out
}

func ParseNote(notedir string, filename string, symlinks []string) (Note, error) {
        var note = Note{}
        note.Filename = filename

        f, err := os.Open(filename)
        if err != nil {
                return note, err
        }

        defer f.Close()
        reader := bufio.NewReaderSize(f, 4*1024)

        line, prefix, err := reader.ReadLine()
        if err != nil {
                return note, err
        }

        if prefix {
                return note, errors.New("Buffer reader too small for the name of the note.")
        }

        note.Name = string(line)

        note.Tags = findTags(filename, notedir, symlinks)

        return note, nil
}

func ParseDir(notedir string) ([]Note, error) {
        var notes []Note

        notedir, _ = filepath.Abs(notedir)
        files, symlinks := readFilesInDir(notedir, "")

        for _, f := range files {
                note, err := ParseNote(notedir, f, symlinks)
                if err != nil {
                        return nil, err
                }

                notes = append(notes, note)
        }

        return notes, nil
}

func CreateNote(title string, tag string, dir string) error {
        spacer := "\n" + strings.Repeat("=", len(title))
        text := title + spacer

        directory := fmt.Sprintf("%s/%s", dir, tag)
        os.MkdirAll(directory, 755)

        file := fmt.Sprintf("%s/%s.rst", directory, TitleToFilename(title))

        if _, err := os.Stat(file); err == nil {
                return errors.New("Notefile already exists. " +
                                "You can still edit it if you want.")
        }

        e := ioutil.WriteFile(file, []byte(text), 0644)
        if e != nil {
                return e
        }

        return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 The etcd 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.

// +build !cluster_proxy

package integration

import (
	"context"
	"testing"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
	"github.com/coreos/etcd/integration"
	"github.com/coreos/etcd/pkg/testutil"
)

// TestBalancerUnderNetworkPartitionPut tests when one member becomes isolated,
// first Put request fails, and following retry succeeds with client balancer
// switching to others.
func TestBalancerUnderNetworkPartitionPut(t *testing.T) {
	testBalancerUnderNetworkPartition(t, func(cli *clientv3.Client, ctx context.Context) error {
		_, err := cli.Put(ctx, "a", "b")
		return err
	})
}

// TestBalancerUnderNetworkPartitionGet tests when one member becomes isolated,
// first Get request fails, and following retry succeeds with client balancer
// switching to others.
func TestBalancerUnderNetworkPartitionGet(t *testing.T) {
	testBalancerUnderNetworkPartition(t, func(cli *clientv3.Client, ctx context.Context) error {
		_, err := cli.Get(ctx, "a")
		return err
	})
}

func testBalancerUnderNetworkPartition(t *testing.T, op func(*clientv3.Client, context.Context) error) {
	defer testutil.AfterTest(t)

	clus := integration.NewClusterV3(t, &integration.ClusterConfig{
		Size:                 3,
		GRPCKeepAliveMinTime: time.Millisecond, // avoid too_many_pings
		SkipCreatingClient:   true,
	})
	defer clus.Terminate(t)

	// expect pin ep[0]
	ccfg := clientv3.Config{
		Endpoints:            []string{clus.Members[0].GRPCAddr()},
		DialTimeout:          3 * time.Second,
		DialKeepAliveTime:    2 * time.Second,
		DialKeepAliveTimeout: 2 * time.Second,
	}
	cli, err := clientv3.New(ccfg)
	if err != nil {
		t.Fatal(err)
	}
	defer cli.Close()

	// wait for ep[0] to be pinned
	waitPinReady(t, cli)

	// add other endpoints for later endpoint switch
	cli.SetEndpoints(clus.Members[0].GRPCAddr(), clus.Members[1].GRPCAddr(), clus.Members[2].GRPCAddr())
	clus.Members[0].InjectPartition(t, clus.Members[1:]...)

	for i := 0; i < 2; i++ {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		err = op(cli, ctx)
		cancel()
		if err == nil {
			break
		}
		// TODO: separate put and get test for error checking.
		// we do not really expect ErrTimeout on get.
		if err != context.DeadlineExceeded && err != rpctypes.ErrTimeout {
			t.Errorf("#%d: expected %v or %v, got %v", i, context.DeadlineExceeded, rpctypes.ErrTimeout, err)
		}
		// give enough time for endpoint switch
		// TODO: remove random sleep by syncing directly with balancer
		if i == 0 {
			time.Sleep(5 * time.Second)
		}
	}
	if err != nil {
		t.Errorf("balancer did not switch in time (%v)", err)
	}
}
<commit_msg>clientv3/integration: add TestBalancerUnderNetworkPartitionWatch<commit_after>// Copyright 2017 The etcd 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.

// +build !cluster_proxy

package integration

import (
	"context"
	"testing"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
	"github.com/coreos/etcd/integration"
	"github.com/coreos/etcd/pkg/testutil"
)

// TestBalancerUnderNetworkPartitionPut tests when one member becomes isolated,
// first Put request fails, and following retry succeeds with client balancer
// switching to others.
func TestBalancerUnderNetworkPartitionPut(t *testing.T) {
	testBalancerUnderNetworkPartition(t, func(cli *clientv3.Client, ctx context.Context) error {
		_, err := cli.Put(ctx, "a", "b")
		return err
	})
}

// TestBalancerUnderNetworkPartitionGet tests when one member becomes isolated,
// first Get request fails, and following retry succeeds with client balancer
// switching to others.
func TestBalancerUnderNetworkPartitionGet(t *testing.T) {
	testBalancerUnderNetworkPartition(t, func(cli *clientv3.Client, ctx context.Context) error {
		_, err := cli.Get(ctx, "a")
		return err
	})
}

func testBalancerUnderNetworkPartition(t *testing.T, op func(*clientv3.Client, context.Context) error) {
	defer testutil.AfterTest(t)

	clus := integration.NewClusterV3(t, &integration.ClusterConfig{
		Size:                 3,
		GRPCKeepAliveMinTime: time.Millisecond, // avoid too_many_pings
		SkipCreatingClient:   true,
	})
	defer clus.Terminate(t)

	// expect pin ep[0]
	ccfg := clientv3.Config{
		Endpoints:            []string{clus.Members[0].GRPCAddr()},
		DialTimeout:          3 * time.Second,
		DialKeepAliveTime:    2 * time.Second,
		DialKeepAliveTimeout: 2 * time.Second,
	}
	cli, err := clientv3.New(ccfg)
	if err != nil {
		t.Fatal(err)
	}
	defer cli.Close()

	// wait for ep[0] to be pinned
	waitPinReady(t, cli)

	// add other endpoints for later endpoint switch
	cli.SetEndpoints(clus.Members[0].GRPCAddr(), clus.Members[1].GRPCAddr(), clus.Members[2].GRPCAddr())
	clus.Members[0].InjectPartition(t, clus.Members[1:]...)

	for i := 0; i < 2; i++ {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		err = op(cli, ctx)
		cancel()
		if err == nil {
			break
		}
		// TODO: separate put and get test for error checking.
		// we do not really expect ErrTimeout on get.
		if err != context.DeadlineExceeded && err != rpctypes.ErrTimeout {
			t.Errorf("#%d: expected %v or %v, got %v", i, context.DeadlineExceeded, rpctypes.ErrTimeout, err)
		}
		// give enough time for endpoint switch
		// TODO: remove random sleep by syncing directly with balancer
		if i == 0 {
			time.Sleep(5 * time.Second)
		}
	}
	if err != nil {
		t.Errorf("balancer did not switch in time (%v)", err)
	}
}

func TestBalancerUnderNetworkPartitionWatchLeader(t *testing.T) {
	testBalancerUnderNetworkPartitionWatch(t, true)
}

func TestBalancerUnderNetworkPartitionWatchFollower(t *testing.T) {
	testBalancerUnderNetworkPartitionWatch(t, false)
}

// testBalancerUnderNetworkPartitionWatch ensures watch stream
// to a partitioned node be closed when context requires leader.
func testBalancerUnderNetworkPartitionWatch(t *testing.T, isolateLeader bool) {
	defer testutil.AfterTest(t)

	clus := integration.NewClusterV3(t, &integration.ClusterConfig{
		Size:               3,
		SkipCreatingClient: true,
	})
	defer clus.Terminate(t)

	eps := []string{clus.Members[0].GRPCAddr(), clus.Members[1].GRPCAddr(), clus.Members[2].GRPCAddr()}

	target := clus.WaitLeader(t)
	if !isolateLeader {
		target = (target + 1) % 3
	}

	// pin eps[target]
	watchCli, err := clientv3.New(clientv3.Config{Endpoints: []string{eps[target]}})
	if err != nil {
		t.Fatal(err)
	}
	defer watchCli.Close()

	// wait for eps[target] to be pinned
	waitPinReady(t, watchCli)

	// add all eps to list, so that when the original pined one fails
	// the client can switch to other available eps
	watchCli.SetEndpoints(eps...)

	wch := watchCli.Watch(clientv3.WithRequireLeader(context.Background()), "foo", clientv3.WithCreatedNotify())
	select {
	case <-wch:
	case <-time.After(3 * time.Second):
		t.Fatal("took too long to create watch")
	}

	// isolate eps[target]
	clus.Members[target].InjectPartition(t,
		clus.Members[(target+1)%3],
		clus.Members[(target+2)%3],
	)

	select {
	case ev := <-wch:
		if len(ev.Events) != 0 {
			t.Fatal("expected no event")
		}
		if err = ev.Err(); err != rpctypes.ErrNoLeader {
			t.Fatalf("expected %v, got %v", rpctypes.ErrNoLeader, err)
		}
	case <-time.After(3 * time.Second): // enough time to detect leader lost
		t.Fatal("took too long to detect leader lost")
	}
}
<|endoftext|>


<commit_before>package config

import (
	"context"
	"fmt"
	"github.com/apex/log"
	"github.com/pkg/errors"
	"html/template"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"time"
)

// Defines basic system configuration settings.
type SystemConfiguration struct {
	// The root directory where all of the pterodactyl data is stored at.
	RootDirectory string `default:"/var/lib/pterodactyl" yaml:"root_directory"`

	// Directory where logs for server installations and other wings events are logged.
	LogDirectory string `default:"/var/log/pterodactyl" yaml:"log_directory"`

	// Directory where the server data is stored at.
	Data string `default:"/var/lib/pterodactyl/volumes" yaml:"data"`

	// Directory where server archives for transferring will be stored.
	ArchiveDirectory string `default:"/var/lib/pterodactyl/archives" yaml:"archive_directory"`

	// Directory where local backups will be stored on the machine.
	BackupDirectory string `default:"/var/lib/pterodactyl/backups" yaml:"backup_directory"`

	// The user that should own all of the server files, and be used for containers.
	Username string `default:"pterodactyl" yaml:"username"`

	// The timezone for this Wings instance. This is detected by Wings automatically if possible,
	// and falls back to UTC if not able to be detected. If you need to set this manually, that
	// can also be done.
	//
	// This timezone value is passed into all containers created by Wings.
	Timezone string `yaml:"timezone"`

	// Definitions for the user that gets created to ensure that we can quickly access
	// this information without constantly having to do a system lookup.
	User struct {
		Uid int
		Gid int
	}

	// The amount of time in seconds that can elapse before a server's disk space calculation is
	// considered stale and a re-check should occur. DANGER: setting this value too low can seriously
	// impact system performance and cause massive I/O bottlenecks and high CPU usage for the Wings
	// process.
	DiskCheckInterval int64 `default:"150" yaml:"disk_check_interval"`

	// Determines if Wings should detect a server that stops with a normal exit code of
	// "0" as being crashed if the process stopped without any Wings interaction. E.g.
	// the user did not press the stop button, but the process stopped cleanly.
	DetectCleanExitAsCrash bool `default:"true" yaml:"detect_clean_exit_as_crash"`

	// If set to true, file permissions for a server will be checked when the process is
	// booted. This can cause boot delays if the server has a large amount of files. In most
	// cases disabling this should not have any major impact unless external processes are
	// frequently modifying a servers' files.
	CheckPermissionsOnBoot bool `default:"true" yaml:"check_permissions_on_boot"`

	// If set to false Wings will not attempt to write a log rotate configuration to the disk
	// when it boots and one is not detected.
	EnableLogRotate bool `default:"true" yaml:"enable_log_rotate"`

	Sftp SftpConfiguration `yaml:"sftp"`
}

// Ensures that all of the system directories exist on the system. These directories are
// created so that only the owner can read the data, and no other users.
func (sc *SystemConfiguration) ConfigureDirectories() error {
	log.WithField("path", sc.RootDirectory).Debug("ensuring root data directory exists")
	if err := os.MkdirAll(sc.RootDirectory, 0700); err != nil {
		return err
	}

	// There are a non-trivial number of users out there whose data directories are actually a
	// symlink to another location on the disk. If we do not resolve that final destination at this
	// point things will appear to work, but endless errors will be encountered when we try to
	// verify accessed paths since they will all end up resolving outside the expected data directory.
	//
	// For the sake of automating away as much of this as possible, see if the data directory is a
	// symlink, and if so resolve to its final real path, and then update the configuration to use
	// that.
	if d, err := filepath.EvalSymlinks(sc.Data); err != nil {
		if !os.IsNotExist(err) {
			return errors.WithStack(err)
		}
	} else if d != sc.Data {
		sc.Data = d
	}

	log.WithField("path", sc.Data).Debug("ensuring server data directory exists")
	if err := os.MkdirAll(sc.Data, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.ArchiveDirectory).Debug("ensuring archive data directory exists")
	if err := os.MkdirAll(sc.ArchiveDirectory, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.BackupDirectory).Debug("ensuring backup data directory exists")
	if err := os.MkdirAll(sc.BackupDirectory, 0700); err != nil {
		return err
	}

	return nil
}

// Writes a logrotate file for wings to the system logrotate configuration directory if one
// exists and a logrotate file is not found. This allows us to basically automate away the log
// rotation for most installs, but also enable users to make modifications on their own.
func (sc *SystemConfiguration) EnableLogRotation() error {
	// Do nothing if not enabled.
	if sc.EnableLogRotate == false {
		log.Info("skipping log rotate configuration, disabled in wings config file")

		return nil
	}

	if st, err := os.Stat("/etc/logrotate.d"); err != nil && !os.IsNotExist(err) {
		return errors.WithStack(err)
	} else if (err != nil && os.IsNotExist(err)) || !st.IsDir() {
		return nil
	}

	if _, err := os.Stat("/etc/logrotate.d/wings"); err != nil && !os.IsNotExist(err) {
		return errors.WithStack(err)
	} else if err == nil {
		return nil
	}

	log.Info("no log rotation configuration found, system is configured to support it, adding file now")
	// If we've gotten to this point it means the logrotate directory exists on the system
	// but there is not a file for wings already. In that case, let us write a new file to
	// it so files can be rotated easily.
	f, err := os.Create("/etc/logrotate.d/wings")
	if err != nil {
		return errors.WithStack(err)
	}
	defer f.Close()

	t, err := template.New("logrotate").Parse(`
{{.LogDirectory}}/wings.log {
    size 10M
    compress
    delaycompress
    dateext
    maxage 7
    missingok
    notifempty
    create 0640 {{.User.Uid}} {{.User.Gid}}
    postrotate
        killall -SIGHUP wings
    endscript
}`)

	if err != nil {
		return errors.WithStack(err)
	}

	return errors.Wrap(t.Execute(f, sc), "failed to write logrotate file to disk")
}

// Returns the location of the JSON file that tracks server states.
func (sc *SystemConfiguration) GetStatesPath() string {
	return path.Join(sc.RootDirectory, "states.json")
}

// Returns the location of the JSON file that tracks server states.
func (sc *SystemConfiguration) GetInstallLogPath() string {
	return path.Join(sc.LogDirectory, "install/")
}

// Configures the timezone data for the configuration if it is currently missing. If
// a value has been set, this functionality will only run to validate that the timezone
// being used is valid.
func (sc *SystemConfiguration) ConfigureTimezone() error {
	if sc.Timezone == "" {
		if b, err := ioutil.ReadFile("/etc/timezone"); err != nil {
			if !os.IsNotExist(err) {
				return errors.Wrap(err, "failed to open /etc/timezone for automatic server timezone calibration")
			}

			ctx, _ := context.WithTimeout(context.Background(), time.Second * 5)
			// Okay, file isn't found on this OS, we will try using timedatectl to handle this. If this
			// command fails, exit, but if it returns a value use that. If no value is returned we will
			// fall through to UTC to get Wings booted at least.
			out, err := exec.CommandContext(ctx, "timedatectl").Output()
			if err != nil {
				log.WithField("error", err).Warn("failed to execute \"timedatectl\" to determine system timezone, falling back to UTC")

				sc.Timezone = "UTC"
				return nil
			}

			r := regexp.MustCompile(`Time zone: ([\w/]+)`)
			matches := r.FindSubmatch(out)
			if len(matches) != 2 || string(matches[1]) == "" {
				log.Warn("failed to parse timezone from \"timedatectl\" output, falling back to UTC")

				sc.Timezone = "UTC"
				return nil
			}

			sc.Timezone = string(matches[1])
		} else {
			sc.Timezone = string(b)
		}
	}

	sc.Timezone = regexp.MustCompile(`[^a-z_/]+/ig`).ReplaceAllString(sc.Timezone, "")

	_, err := time.LoadLocation(sc.Timezone)

	return errors.Wrap(err, fmt.Sprintf("the supplied timezone %s is invalid", sc.Timezone))
}<commit_msg>Use correct case-insensitive regex; closes pterodactyl/panel#2546<commit_after>package config

import (
	"context"
	"fmt"
	"github.com/apex/log"
	"github.com/pkg/errors"
	"html/template"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"time"
)

// Defines basic system configuration settings.
type SystemConfiguration struct {
	// The root directory where all of the pterodactyl data is stored at.
	RootDirectory string `default:"/var/lib/pterodactyl" yaml:"root_directory"`

	// Directory where logs for server installations and other wings events are logged.
	LogDirectory string `default:"/var/log/pterodactyl" yaml:"log_directory"`

	// Directory where the server data is stored at.
	Data string `default:"/var/lib/pterodactyl/volumes" yaml:"data"`

	// Directory where server archives for transferring will be stored.
	ArchiveDirectory string `default:"/var/lib/pterodactyl/archives" yaml:"archive_directory"`

	// Directory where local backups will be stored on the machine.
	BackupDirectory string `default:"/var/lib/pterodactyl/backups" yaml:"backup_directory"`

	// The user that should own all of the server files, and be used for containers.
	Username string `default:"pterodactyl" yaml:"username"`

	// The timezone for this Wings instance. This is detected by Wings automatically if possible,
	// and falls back to UTC if not able to be detected. If you need to set this manually, that
	// can also be done.
	//
	// This timezone value is passed into all containers created by Wings.
	Timezone string `yaml:"timezone"`

	// Definitions for the user that gets created to ensure that we can quickly access
	// this information without constantly having to do a system lookup.
	User struct {
		Uid int
		Gid int
	}

	// The amount of time in seconds that can elapse before a server's disk space calculation is
	// considered stale and a re-check should occur. DANGER: setting this value too low can seriously
	// impact system performance and cause massive I/O bottlenecks and high CPU usage for the Wings
	// process.
	DiskCheckInterval int64 `default:"150" yaml:"disk_check_interval"`

	// Determines if Wings should detect a server that stops with a normal exit code of
	// "0" as being crashed if the process stopped without any Wings interaction. E.g.
	// the user did not press the stop button, but the process stopped cleanly.
	DetectCleanExitAsCrash bool `default:"true" yaml:"detect_clean_exit_as_crash"`

	// If set to true, file permissions for a server will be checked when the process is
	// booted. This can cause boot delays if the server has a large amount of files. In most
	// cases disabling this should not have any major impact unless external processes are
	// frequently modifying a servers' files.
	CheckPermissionsOnBoot bool `default:"true" yaml:"check_permissions_on_boot"`

	// If set to false Wings will not attempt to write a log rotate configuration to the disk
	// when it boots and one is not detected.
	EnableLogRotate bool `default:"true" yaml:"enable_log_rotate"`

	Sftp SftpConfiguration `yaml:"sftp"`
}

// Ensures that all of the system directories exist on the system. These directories are
// created so that only the owner can read the data, and no other users.
func (sc *SystemConfiguration) ConfigureDirectories() error {
	log.WithField("path", sc.RootDirectory).Debug("ensuring root data directory exists")
	if err := os.MkdirAll(sc.RootDirectory, 0700); err != nil {
		return err
	}

	// There are a non-trivial number of users out there whose data directories are actually a
	// symlink to another location on the disk. If we do not resolve that final destination at this
	// point things will appear to work, but endless errors will be encountered when we try to
	// verify accessed paths since they will all end up resolving outside the expected data directory.
	//
	// For the sake of automating away as much of this as possible, see if the data directory is a
	// symlink, and if so resolve to its final real path, and then update the configuration to use
	// that.
	if d, err := filepath.EvalSymlinks(sc.Data); err != nil {
		if !os.IsNotExist(err) {
			return errors.WithStack(err)
		}
	} else if d != sc.Data {
		sc.Data = d
	}

	log.WithField("path", sc.Data).Debug("ensuring server data directory exists")
	if err := os.MkdirAll(sc.Data, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.ArchiveDirectory).Debug("ensuring archive data directory exists")
	if err := os.MkdirAll(sc.ArchiveDirectory, 0700); err != nil {
		return err
	}

	log.WithField("path", sc.BackupDirectory).Debug("ensuring backup data directory exists")
	if err := os.MkdirAll(sc.BackupDirectory, 0700); err != nil {
		return err
	}

	return nil
}

// Writes a logrotate file for wings to the system logrotate configuration directory if one
// exists and a logrotate file is not found. This allows us to basically automate away the log
// rotation for most installs, but also enable users to make modifications on their own.
func (sc *SystemConfiguration) EnableLogRotation() error {
	// Do nothing if not enabled.
	if sc.EnableLogRotate == false {
		log.Info("skipping log rotate configuration, disabled in wings config file")

		return nil
	}

	if st, err := os.Stat("/etc/logrotate.d"); err != nil && !os.IsNotExist(err) {
		return errors.WithStack(err)
	} else if (err != nil && os.IsNotExist(err)) || !st.IsDir() {
		return nil
	}

	if _, err := os.Stat("/etc/logrotate.d/wings"); err != nil && !os.IsNotExist(err) {
		return errors.WithStack(err)
	} else if err == nil {
		return nil
	}

	log.Info("no log rotation configuration found, system is configured to support it, adding file now")
	// If we've gotten to this point it means the logrotate directory exists on the system
	// but there is not a file for wings already. In that case, let us write a new file to
	// it so files can be rotated easily.
	f, err := os.Create("/etc/logrotate.d/wings")
	if err != nil {
		return errors.WithStack(err)
	}
	defer f.Close()

	t, err := template.New("logrotate").Parse(`
{{.LogDirectory}}/wings.log {
    size 10M
    compress
    delaycompress
    dateext
    maxage 7
    missingok
    notifempty
    create 0640 {{.User.Uid}} {{.User.Gid}}
    postrotate
        killall -SIGHUP wings
    endscript
}`)

	if err != nil {
		return errors.WithStack(err)
	}

	return errors.Wrap(t.Execute(f, sc), "failed to write logrotate file to disk")
}

// Returns the location of the JSON file that tracks server states.
func (sc *SystemConfiguration) GetStatesPath() string {
	return path.Join(sc.RootDirectory, "states.json")
}

// Returns the location of the JSON file that tracks server states.
func (sc *SystemConfiguration) GetInstallLogPath() string {
	return path.Join(sc.LogDirectory, "install/")
}

// Configures the timezone data for the configuration if it is currently missing. If
// a value has been set, this functionality will only run to validate that the timezone
// being used is valid.
func (sc *SystemConfiguration) ConfigureTimezone() error {
	if sc.Timezone == "" {
		if b, err := ioutil.ReadFile("/etc/timezone"); err != nil {
			if !os.IsNotExist(err) {
				return errors.Wrap(err, "failed to open /etc/timezone for automatic server timezone calibration")
			}

			ctx, _ := context.WithTimeout(context.Background(), time.Second * 5)
			// Okay, file isn't found on this OS, we will try using timedatectl to handle this. If this
			// command fails, exit, but if it returns a value use that. If no value is returned we will
			// fall through to UTC to get Wings booted at least.
			out, err := exec.CommandContext(ctx, "timedatectl").Output()
			if err != nil {
				log.WithField("error", err).Warn("failed to execute \"timedatectl\" to determine system timezone, falling back to UTC")

				sc.Timezone = "UTC"
				return nil
			}

			r := regexp.MustCompile(`Time zone: ([\w/]+)`)
			matches := r.FindSubmatch(out)
			if len(matches) != 2 || string(matches[1]) == "" {
				log.Warn("failed to parse timezone from \"timedatectl\" output, falling back to UTC")

				sc.Timezone = "UTC"
				return nil
			}

			sc.Timezone = string(matches[1])
		} else {
			sc.Timezone = string(b)
		}
	}

	sc.Timezone = regexp.MustCompile(`(?i)[^a-z_/]+`).ReplaceAllString(sc.Timezone, "")

	_, err := time.LoadLocation(sc.Timezone)

	return errors.Wrap(err, fmt.Sprintf("the supplied timezone %s is invalid", sc.Timezone))
}<|endoftext|>


<commit_before>package main

import (
	"appengine"
	"appengine/datastore"
	"html/template"
	"net/http"
	"os"
)

type Answer struct {
	Value string
}

type Password struct {
	Value string
}

func init() {
	http.HandleFunc("/", getAnswer)
	http.HandleFunc("/yes", setAnswer("yes"))
	http.HandleFunc("/no", setAnswer("no"))
}

func getAnswer(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	k := datastore.NewKey(c, "Answer", "answer", 0, nil)
	a := new(Answer)
	if err := datastore.Get(c, k, a); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	t := template.Must(template.ParseFiles("index.template"))
	if err := t.Execute(w, a); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func setAnswer(answer string) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		if _, p, ok := r.BasicAuth(); !ok || p != os.Getenv("PASSWORD") {
			w.Header().Set("WWW-Authenticate", "Basic")
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}
		c := appengine.NewContext(r)
		k := datastore.NewKey(c, "Answer", "answer", 0, nil)
		a := Answer{
			Value: answer,
		}
		if _, err := datastore.Put(c, k, &a); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		http.Redirect(w, r, "/", http.StatusFound)
	}
}
<commit_msg>update import paths for go 1.11<commit_after>package main

import (
	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"html/template"
	"net/http"
	"os"
)

type Answer struct {
	Value string
}

type Password struct {
	Value string
}

func init() {
	http.HandleFunc("/", getAnswer)
	http.HandleFunc("/yes", setAnswer("yes"))
	http.HandleFunc("/no", setAnswer("no"))
}

func getAnswer(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	k := datastore.NewKey(c, "Answer", "answer", 0, nil)
	a := new(Answer)
	if err := datastore.Get(c, k, a); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	t := template.Must(template.ParseFiles("index.template"))
	if err := t.Execute(w, a); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func setAnswer(answer string) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		if _, p, ok := r.BasicAuth(); !ok || p != os.Getenv("PASSWORD") {
			w.Header().Set("WWW-Authenticate", "Basic")
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}
		c := appengine.NewContext(r)
		k := datastore.NewKey(c, "Answer", "answer", 0, nil)
		a := Answer{
			Value: answer,
		}
		if _, err := datastore.Put(c, k, &a); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		http.Redirect(w, r, "/", http.StatusFound)
	}
}
<|endoftext|>


<commit_before>package notifiers

import (
	"fmt"
	"net/url"
	"strconv"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

const PUSHOVER_ENDPOINT = "https://api.pushover.net/1/messages.json"

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "pushover",
		Name:        "Pushover",
		Description: "Sends HTTP POST request to the Pushover API",
		Factory:     NewPushoverNotifier,
		OptionsTemplate: `

Pushover settings

        API Token
        <input type="text" class="gf-form-input" required placeholder="Application token" ng-model="ctrl.model.settings.apiToken"></input>

        User key(s)
        <input type="text" class="gf-form-input" required placeholder="comma-separated list" ng-model="ctrl.model.settings.userKey"></input>

        Device(s) (optional)
        <input type="text" class="gf-form-input" placeholder="comma-separated list; leave empty to send to all devices" ng-model="ctrl.model.settings.device"></input>

        Priority
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.priority" ng-options="v as k for (k, v) in {
          Emergency: '2',
          High:      '1',
          Normal:    '0',
          Low:      '-1',
          Lowest:   '-2'
        }" ng-init="ctrl.model.settings.priority=ctrl.model.settings.priority||'0'"></select>

        Retry
        <input type="text" class="gf-form-input max-width-14" ng-required="ctrl.model.settings.priority == '2'" placeholder="minimum 30 seconds" ng-model="ctrl.model.settings.retry" ng-init="ctrl.model.settings.retry=ctrl.model.settings.retry||'60'></input>

        Expire
        <input type="text" class="gf-form-input max-width-14" ng-required="ctrl.model.settings.priority == '2'" placeholder="maximum 86400 seconds" ng-model="ctrl.model.settings.expire" ng-init="ctrl.model.settings.expire=ctrl.model.settings.expire||'3600'"></input>

        Sound
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.sound" ng-options="s for s in [
          'default',
          'pushover',
          'bike',
          'bugle',
          'cashregister',
          'classical',
          'cosmic',
          'falling',
          'gamelan',
          'incoming',
          'intermission',
          'magic',
          'mechanical',
          'pianobar',
          'siren',
          'spacealarm',
          'tugboat',
          'alien',
          'climb',
          'persistent',
          'echo',
          'updown',
          'none'
        ]" ng-init="ctrl.model.settings.sound=ctrl.model.settings.sound||'default'"></select>

    `,
	})
}

func NewPushoverNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	userKey := model.Settings.Get("userKey").MustString()
	apiToken := model.Settings.Get("apiToken").MustString()
	device := model.Settings.Get("device").MustString()
	priority, _ := strconv.Atoi(model.Settings.Get("priority").MustString())
	retry, _ := strconv.Atoi(model.Settings.Get("retry").MustString())
	expire, _ := strconv.Atoi(model.Settings.Get("expire").MustString())
	sound := model.Settings.Get("sound").MustString()

	if userKey == "" {
		return nil, alerting.ValidationError{Reason: "User key not given"}
	}
	if apiToken == "" {
		return nil, alerting.ValidationError{Reason: "API token not given"}
	}
	return &PushoverNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		UserKey:      userKey,
		ApiToken:     apiToken,
		Priority:     priority,
		Retry:        retry,
		Expire:       expire,
		Device:       device,
		Sound:        sound,
		log:          log.New("alerting.notifier.pushover"),
	}, nil
}

type PushoverNotifier struct {
	NotifierBase
	UserKey  string
	ApiToken string
	Priority int
	Retry    int
	Expire   int
	Device   string
	Sound    string
	log      log.Logger
}

func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
	ruleUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed get rule link", "error", err)
		return err
	}
	message := evalContext.Rule.Message
	for idx, evt := range evalContext.EvalMatches {
		message += fmt.Sprintf("\n%s: %v", evt.Metric, evt.Value)
		if idx > 4 {
			break
		}
	}
	if evalContext.Error != nil {
		message += fmt.Sprintf("\nError message: %s", evalContext.Error.Error())
	}
	if evalContext.ImagePublicUrl != "" {
		message += fmt.Sprintf("\nShow graph image", evalContext.ImagePublicUrl)
	}

	q := url.Values{}
	q.Add("user", this.UserKey)
	q.Add("token", this.ApiToken)
	q.Add("priority", strconv.Itoa(this.Priority))
	if this.Priority == 2 {
		q.Add("retry", strconv.Itoa(this.Retry))
		q.Add("expire", strconv.Itoa(this.Expire))
	}
	if this.Device != "" {
		q.Add("device", this.Device)
	}
	if this.Sound != "default" {
		q.Add("sound", this.Sound)
	}
	q.Add("title", evalContext.GetNotificationTitle())
	q.Add("url", ruleUrl)
	q.Add("url_title", "Show dashboard with alert")
	q.Add("message", message)
	q.Add("html", "1")

	cmd := &m.SendWebhookSync{
		Url:        PUSHOVER_ENDPOINT,
		HttpMethod: "POST",
		HttpHeader: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
		Body:       q.Encode(),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send pushover notification", "error", err, "webhook", this.Name)
		return err
	}

	return nil
}
<commit_msg>Add default message for Pushover notifications<commit_after>package notifiers

import (
	"fmt"
	"net/url"
	"strconv"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

const PUSHOVER_ENDPOINT = "https://api.pushover.net/1/messages.json"

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "pushover",
		Name:        "Pushover",
		Description: "Sends HTTP POST request to the Pushover API",
		Factory:     NewPushoverNotifier,
		OptionsTemplate: `

Pushover settings

        API Token
        <input type="text" class="gf-form-input" required placeholder="Application token" ng-model="ctrl.model.settings.apiToken"></input>

        User key(s)
        <input type="text" class="gf-form-input" required placeholder="comma-separated list" ng-model="ctrl.model.settings.userKey"></input>

        Device(s) (optional)
        <input type="text" class="gf-form-input" placeholder="comma-separated list; leave empty to send to all devices" ng-model="ctrl.model.settings.device"></input>

        Priority
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.priority" ng-options="v as k for (k, v) in {
          Emergency: '2',
          High:      '1',
          Normal:    '0',
          Low:      '-1',
          Lowest:   '-2'
        }" ng-init="ctrl.model.settings.priority=ctrl.model.settings.priority||'0'"></select>

        Retry
        <input type="text" class="gf-form-input max-width-14" ng-required="ctrl.model.settings.priority == '2'" placeholder="minimum 30 seconds" ng-model="ctrl.model.settings.retry" ng-init="ctrl.model.settings.retry=ctrl.model.settings.retry||'60'></input>

        Expire
        <input type="text" class="gf-form-input max-width-14" ng-required="ctrl.model.settings.priority == '2'" placeholder="maximum 86400 seconds" ng-model="ctrl.model.settings.expire" ng-init="ctrl.model.settings.expire=ctrl.model.settings.expire||'3600'"></input>

        Sound
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.sound" ng-options="s for s in [
          'default',
          'pushover',
          'bike',
          'bugle',
          'cashregister',
          'classical',
          'cosmic',
          'falling',
          'gamelan',
          'incoming',
          'intermission',
          'magic',
          'mechanical',
          'pianobar',
          'siren',
          'spacealarm',
          'tugboat',
          'alien',
          'climb',
          'persistent',
          'echo',
          'updown',
          'none'
        ]" ng-init="ctrl.model.settings.sound=ctrl.model.settings.sound||'default'"></select>

    `,
	})
}

func NewPushoverNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	userKey := model.Settings.Get("userKey").MustString()
	apiToken := model.Settings.Get("apiToken").MustString()
	device := model.Settings.Get("device").MustString()
	priority, _ := strconv.Atoi(model.Settings.Get("priority").MustString())
	retry, _ := strconv.Atoi(model.Settings.Get("retry").MustString())
	expire, _ := strconv.Atoi(model.Settings.Get("expire").MustString())
	sound := model.Settings.Get("sound").MustString()

	if userKey == "" {
		return nil, alerting.ValidationError{Reason: "User key not given"}
	}
	if apiToken == "" {
		return nil, alerting.ValidationError{Reason: "API token not given"}
	}
	return &PushoverNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		UserKey:      userKey,
		ApiToken:     apiToken,
		Priority:     priority,
		Retry:        retry,
		Expire:       expire,
		Device:       device,
		Sound:        sound,
		log:          log.New("alerting.notifier.pushover"),
	}, nil
}

type PushoverNotifier struct {
	NotifierBase
	UserKey  string
	ApiToken string
	Priority int
	Retry    int
	Expire   int
	Device   string
	Sound    string
	log      log.Logger
}

func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
	ruleUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed get rule link", "error", err)
		return err
	}

	message := evalContext.Rule.Message
	for idx, evt := range evalContext.EvalMatches {
		message += fmt.Sprintf("\n%s: %v", evt.Metric, evt.Value)
		if idx > 4 {
			break
		}
	}
	if evalContext.Error != nil {
		message += fmt.Sprintf("\nError message: %s", evalContext.Error.Error())
	}
	if evalContext.ImagePublicUrl != "" {
		message += fmt.Sprintf("\nShow graph image", evalContext.ImagePublicUrl)
	}
	if message == "" {
		message = "Nothing to see here! (Set a notification message to replace this text.)"
	}

	q := url.Values{}
	q.Add("user", this.UserKey)
	q.Add("token", this.ApiToken)
	q.Add("priority", strconv.Itoa(this.Priority))
	if this.Priority == 2 {
		q.Add("retry", strconv.Itoa(this.Retry))
		q.Add("expire", strconv.Itoa(this.Expire))
	}
	if this.Device != "" {
		q.Add("device", this.Device)
	}
	if this.Sound != "default" {
		q.Add("sound", this.Sound)
	}
	q.Add("title", evalContext.GetNotificationTitle())
	q.Add("url", ruleUrl)
	q.Add("url_title", "Show dashboard with alert")
	q.Add("message", message)
	q.Add("html", "1")

	cmd := &m.SendWebhookSync{
		Url:        PUSHOVER_ENDPOINT,
		HttpMethod: "POST",
		HttpHeader: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
		Body:       q.Encode(),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send pushover notification", "error", err, "webhook", this.Name)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017-2018 Red Hat, Inc.
 *
 */

package watch

import (
	"fmt"
	"reflect"
	"time"

	k8sv1 "k8s.io/api/core/v1"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/wait"

	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/workqueue"

	kubev1 "kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/controller"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/pkg/log"
)

type VirtualMachineInitializer struct {
	vmPresetInformer cache.SharedIndexInformer
	vmInitInformer   cache.SharedIndexInformer
	clientset        kubecli.KubevirtClient
	queue            workqueue.RateLimitingInterface
	recorder         record.EventRecorder
	store            cache.Store
}

const initializerMarking = "presets.virtualmachines.kubevirt.io"

func NewVirtualMachineInitializer(vmPresetInformer cache.SharedIndexInformer, vmInitInformer cache.SharedIndexInformer, queue workqueue.RateLimitingInterface, vmInitCache cache.Store, clientset kubecli.KubevirtClient, recorder record.EventRecorder) *VirtualMachineInitializer {
	vmi := VirtualMachineInitializer{
		vmPresetInformer: vmPresetInformer,
		vmInitInformer:   vmInitInformer,
		clientset:        clientset,
		queue:            queue,
		recorder:         recorder,
		store:            vmInitCache,
	}
	return &vmi
}

func (c *VirtualMachineInitializer) Run(threadiness int, stopCh chan struct{}) {
	defer controller.HandlePanic()
	defer c.queue.ShutDown()
	log.Log.Info("Starting Virtual Machine Initializer.")

	// Wait for cache sync before we start the pod controller
	cache.WaitForCacheSync(stopCh, c.vmPresetInformer.HasSynced, c.vmInitInformer.HasSynced)

	// Start the actual work
	for i := 0; i < threadiness; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}

	<-stopCh
	log.Log.Info("Stopping controller.")
}

func (c *VirtualMachineInitializer) runWorker() {
	for c.Execute() {
	}
}

func (c *VirtualMachineInitializer) Execute() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)
	err := c.execute(key.(string))

	if err != nil {
		log.Log.Reason(err).Infof("reenqueuing VM %v", key)
		c.queue.AddRateLimited(key)
	} else {
		log.Log.V(4).Infof("processed VM %v", key)
		c.queue.Forget(key)
	}
	return true
}

func (c *VirtualMachineInitializer) execute(key string) error {

	// Fetch the latest VM state from cache
	obj, exists, err := c.store.GetByKey(key)

	if err != nil {
		return err
	}

	// If the VM isn't in the cache, it was just deleted, so shouldn't
	// be initialized
	if exists {
		var vm *kubev1.VirtualMachine
		vm = obj.(*kubev1.VirtualMachine)
		// only process VM's that aren't initialized by this controller yet
		if !isInitialized(vm) {
			return c.initializeVirtualMachine(vm)
		}
	}

	return nil
}

func (c *VirtualMachineInitializer) initializeVirtualMachine(vm *kubev1.VirtualMachine) error {
	// All VM's must be marked as initialized or they are held in limbo forever
	// Collect all errors and defer returning until after the update
	logger := log.Log
	var err error

	logger.Object(vm).Info("Initializing VirtualMachine")

	allPresets := listPresets(c.vmPresetInformer, vm.GetNamespace())

	matchingPresets := filterPresets(allPresets, vm, c.recorder)

	if len(matchingPresets) != 0 {
		applyPresets(vm, matchingPresets, c.recorder)
	}

	logger.Object(vm).Info("Marking VM as initialized and updating")
	removeInitializer(vm)
	_, err = c.clientset.VM(vm.Namespace).Update(vm)
	if err != nil {
		logger.Object(vm).Errorf("Could not update VirtualMachine: %v", err)
		return err
	}
	return nil
}

// FIXME: There is probably a way to set up the vmPresetInformer such that
// items are already partitioned into namespaces (and can just be listed)
func listPresets(vmPresetInformer cache.SharedIndexInformer, namespace string) []kubev1.VirtualMachinePreset {
	result := []kubev1.VirtualMachinePreset{}
	for _, obj := range vmPresetInformer.GetStore().List() {
		var preset *kubev1.VirtualMachinePreset
		preset = obj.(*kubev1.VirtualMachinePreset)
		if preset.Namespace == namespace {
			result = append(result, *preset)
		}
	}
	return result
}

// filterPresets returns list of VirtualMachinePresets which match given VirtualMachine.
func filterPresets(list []kubev1.VirtualMachinePreset, vm *kubev1.VirtualMachine, recorder record.EventRecorder) []kubev1.VirtualMachinePreset {
	matchingPresets := []kubev1.VirtualMachinePreset{}

	logger := log.Log

	for _, preset := range list {
		selector, err := k8smetav1.LabelSelectorAsSelector(&preset.Spec.Selector)
		if err != nil {
			// FIXME: create an event here
			// Do not return an error from this function--or the VM will be
			// re-enqueued for processing again.
			recorder.Event(vm, k8sv1.EventTypeWarning, kubev1.PresetFailed.String(), fmt.Sprintf("Invalid Preset '%s': %v", preset.Name, err))
			logger.Object(&preset).Reason(err).Errorf("label selector conversion failed: %v", err)
		} else if selector.Matches(labels.Set(vm.GetLabels())) {
			logger.Object(vm).Infof("VirtualMachinePreset %s matches VirtualMachine", preset.GetName())
			matchingPresets = append(matchingPresets, preset)
		}
	}
	return matchingPresets
}

func checkPresetMergeConflicts(presetSpec *kubev1.DomainSpec, vmSpec *kubev1.DomainSpec) error {
	errors := []error{}
	if len(presetSpec.Resources.Requests) > 0 {
		for key, presetReq := range presetSpec.Resources.Requests {
			if vmReq, ok := vmSpec.Resources.Requests[key]; ok {
				if presetReq != vmReq {
					errors = append(errors, fmt.Errorf("spec.resources.requests[%s]: %v != %v", key, presetReq, vmReq))
				}
			}
		}
	}
	if presetSpec.CPU != nil && vmSpec.CPU != nil {
		if !reflect.DeepEqual(presetSpec.CPU, vmSpec.CPU) {
			errors = append(errors, fmt.Errorf("spec.cpu: %v != %v", presetSpec.CPU, vmSpec.CPU))
		}
	}
	if presetSpec.Firmware != nil && vmSpec.Firmware != nil {
		if !reflect.DeepEqual(presetSpec.Firmware, vmSpec.Firmware) {
			errors = append(errors, fmt.Errorf("spec.firmware: %v != %v", presetSpec.Firmware, vmSpec.Firmware))
		}
	}
	if presetSpec.Clock != nil && vmSpec.Clock != nil {
		if !reflect.DeepEqual(presetSpec.Clock.ClockOffset, vmSpec.Clock.ClockOffset) {
			errors = append(errors, fmt.Errorf("spec.clock.clockoffset: %v != %v", presetSpec.Clock.ClockOffset, vmSpec.Clock.ClockOffset))
		}
		if presetSpec.Clock.Timer != nil && vmSpec.Clock.Timer != nil {
			if !reflect.DeepEqual(presetSpec.Clock.Timer, vmSpec.Clock.Timer) {
				errors = append(errors, fmt.Errorf("spec.clock.timer: %v != %v", presetSpec.Clock.Timer, vmSpec.Clock.Timer))
			}
		}
	}
	if presetSpec.Features != nil && vmSpec.Features != nil {
		if !reflect.DeepEqual(presetSpec.Features, vmSpec.Features) {
			errors = append(errors, fmt.Errorf("spec.features: %v != %v", presetSpec.Features, vmSpec.Features))
		}
	}
	if presetSpec.Devices.Watchdog != nil && vmSpec.Devices.Watchdog != nil {
		if !reflect.DeepEqual(presetSpec.Devices.Watchdog, vmSpec.Devices.Watchdog) {
			errors = append(errors, fmt.Errorf("spec.devices.watchdog: %v != %v", presetSpec.Devices.Watchdog, vmSpec.Devices.Watchdog))
		}
	}

	if len(errors) > 0 {
		return utilerrors.NewAggregate(errors)
	}
	return nil
}

func mergeDomainSpec(presetSpec *kubev1.DomainSpec, vmSpec *kubev1.DomainSpec) (bool, error) {
	presetConflicts := checkPresetMergeConflicts(presetSpec, vmSpec)
	applied := false

	if len(presetSpec.Resources.Requests) > 0 {
		if vmSpec.Resources.Requests == nil {
			vmSpec.Resources.Requests = k8sv1.ResourceList{}
			for key, val := range presetSpec.Resources.Requests {
				vmSpec.Resources.Requests[key] = val
			}
			applied = true
		}
	}
	if presetSpec.CPU != nil {
		if vmSpec.CPU == nil {
			vmSpec.CPU = &kubev1.CPU{}
			presetSpec.CPU.DeepCopyInto(vmSpec.CPU)
			applied = true
		}
	}
	if presetSpec.Firmware != nil {
		if vmSpec.Firmware == nil {
			vmSpec.Firmware = &kubev1.Firmware{}
			presetSpec.Firmware.DeepCopyInto(vmSpec.Firmware)
			applied = true
		}
	}
	if presetSpec.Clock != nil {
		if vmSpec.Clock == nil {
			vmSpec.Clock = &kubev1.Clock{}
			vmSpec.Clock.ClockOffset = presetSpec.Clock.ClockOffset
			applied = true
		}

		if presetSpec.Clock.Timer != nil {
			if vmSpec.Clock.Timer == nil {
				vmSpec.Clock.Timer = &kubev1.Timer{}
				presetSpec.Clock.Timer.DeepCopyInto(vmSpec.Clock.Timer)
				applied = true
			}
		}
	}
	if presetSpec.Features != nil {
		if vmSpec.Features == nil {
			vmSpec.Features = &kubev1.Features{}
			presetSpec.Features.DeepCopyInto(vmSpec.Features)
			applied = true
		}
	}
	if presetSpec.Devices.Watchdog != nil {
		if vmSpec.Devices.Watchdog == nil {
			vmSpec.Devices.Watchdog = &kubev1.Watchdog{}
			presetSpec.Devices.Watchdog.DeepCopyInto(vmSpec.Devices.Watchdog)
			applied = true
		}
	}
	if presetConflicts != nil {
		return applied, presetConflicts
	}
	return applied, nil
}

func applyPresets(vm *kubev1.VirtualMachine, presets []kubev1.VirtualMachinePreset, recorder record.EventRecorder) {
	logger := log.Log
	for _, preset := range presets {
		applied, err := mergeDomainSpec(preset.Spec.Domain, &vm.Spec.Domain)
		if err != nil {
			recorder.Event(vm, k8sv1.EventTypeWarning, kubev1.PresetFailed.String(), fmt.Sprintf("Unable to apply Preset '%s': %v", preset.Name, err))
			logger.Object(vm).Errorf("Unable to apply Preset '%s': %v", preset.Name, err)
		}
		if applied {
			annotateVM(vm, preset)
		}
	}
}

// isInitialized checks if *this* module has initialized the VM,
// which is distinct from "has the VM been initialized by all controllers?"
func isInitialized(vm *kubev1.VirtualMachine) bool {
	// if initializers is nil/empty then consider this resource as initialized
	if vm.Initializers != nil && len(vm.Initializers.Pending) > 0 {
		for _, i := range vm.Initializers.Pending {
			if i.Name == initializerMarking {
				return false
			}
		}
	}
	return true
}

func removeInitializer(vm *kubev1.VirtualMachine) {
	if vm.Initializers == nil {
		// If Initializers is nil, there's nothing to remove.
		return
	}
	newInitilizers := []k8smetav1.Initializer{}
	for _, i := range vm.Initializers.Pending {
		if i.Name != initializerMarking {
			newInitilizers = append(newInitilizers, i)
		}
	}
	vm.Initializers.Pending = newInitilizers
}

func annotateVM(vm *kubev1.VirtualMachine, preset kubev1.VirtualMachinePreset) {
	if vm.Annotations == nil {
		vm.Annotations = map[string]string{}
	}
	annotationKey := fmt.Sprintf("virtualmachinepreset.%s/%s", kubev1.GroupName, preset.Name)
	vm.Annotations[annotationKey] = kubev1.GroupVersion.String()
}
<commit_msg>Remove incorrect comment<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017-2018 Red Hat, Inc.
 *
 */

package watch

import (
	"fmt"
	"reflect"
	"time"

	k8sv1 "k8s.io/api/core/v1"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/wait"

	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/workqueue"

	kubev1 "kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/controller"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/pkg/log"
)

type VirtualMachineInitializer struct {
	vmPresetInformer cache.SharedIndexInformer
	vmInitInformer   cache.SharedIndexInformer
	clientset        kubecli.KubevirtClient
	queue            workqueue.RateLimitingInterface
	recorder         record.EventRecorder
	store            cache.Store
}

const initializerMarking = "presets.virtualmachines.kubevirt.io"

func NewVirtualMachineInitializer(vmPresetInformer cache.SharedIndexInformer, vmInitInformer cache.SharedIndexInformer, queue workqueue.RateLimitingInterface, vmInitCache cache.Store, clientset kubecli.KubevirtClient, recorder record.EventRecorder) *VirtualMachineInitializer {
	vmi := VirtualMachineInitializer{
		vmPresetInformer: vmPresetInformer,
		vmInitInformer:   vmInitInformer,
		clientset:        clientset,
		queue:            queue,
		recorder:         recorder,
		store:            vmInitCache,
	}
	return &vmi
}

func (c *VirtualMachineInitializer) Run(threadiness int, stopCh chan struct{}) {
	defer controller.HandlePanic()
	defer c.queue.ShutDown()
	log.Log.Info("Starting Virtual Machine Initializer.")

	// Wait for cache sync before we start the pod controller
	cache.WaitForCacheSync(stopCh, c.vmPresetInformer.HasSynced, c.vmInitInformer.HasSynced)

	// Start the actual work
	for i := 0; i < threadiness; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}

	<-stopCh
	log.Log.Info("Stopping controller.")
}

func (c *VirtualMachineInitializer) runWorker() {
	for c.Execute() {
	}
}

func (c *VirtualMachineInitializer) Execute() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)
	err := c.execute(key.(string))

	if err != nil {
		log.Log.Reason(err).Infof("reenqueuing VM %v", key)
		c.queue.AddRateLimited(key)
	} else {
		log.Log.V(4).Infof("processed VM %v", key)
		c.queue.Forget(key)
	}
	return true
}

func (c *VirtualMachineInitializer) execute(key string) error {

	// Fetch the latest VM state from cache
	obj, exists, err := c.store.GetByKey(key)

	if err != nil {
		return err
	}

	// If the VM isn't in the cache, it was just deleted, so shouldn't
	// be initialized
	if exists {
		var vm *kubev1.VirtualMachine
		vm = obj.(*kubev1.VirtualMachine)
		// only process VM's that aren't initialized by this controller yet
		if !isInitialized(vm) {
			return c.initializeVirtualMachine(vm)
		}
	}

	return nil
}

func (c *VirtualMachineInitializer) initializeVirtualMachine(vm *kubev1.VirtualMachine) error {
	// All VM's must be marked as initialized or they are held in limbo forever
	// Collect all errors and defer returning until after the update
	logger := log.Log
	var err error

	logger.Object(vm).Info("Initializing VirtualMachine")

	allPresets := listPresets(c.vmPresetInformer, vm.GetNamespace())

	matchingPresets := filterPresets(allPresets, vm, c.recorder)

	if len(matchingPresets) != 0 {
		applyPresets(vm, matchingPresets, c.recorder)
	}

	logger.Object(vm).Info("Marking VM as initialized and updating")
	removeInitializer(vm)
	_, err = c.clientset.VM(vm.Namespace).Update(vm)
	if err != nil {
		logger.Object(vm).Errorf("Could not update VirtualMachine: %v", err)
		return err
	}
	return nil
}

// FIXME: There is probably a way to set up the vmPresetInformer such that
// items are already partitioned into namespaces (and can just be listed)
func listPresets(vmPresetInformer cache.SharedIndexInformer, namespace string) []kubev1.VirtualMachinePreset {
	result := []kubev1.VirtualMachinePreset{}
	for _, obj := range vmPresetInformer.GetStore().List() {
		var preset *kubev1.VirtualMachinePreset
		preset = obj.(*kubev1.VirtualMachinePreset)
		if preset.Namespace == namespace {
			result = append(result, *preset)
		}
	}
	return result
}

// filterPresets returns list of VirtualMachinePresets which match given VirtualMachine.
func filterPresets(list []kubev1.VirtualMachinePreset, vm *kubev1.VirtualMachine, recorder record.EventRecorder) []kubev1.VirtualMachinePreset {
	matchingPresets := []kubev1.VirtualMachinePreset{}

	logger := log.Log

	for _, preset := range list {
		selector, err := k8smetav1.LabelSelectorAsSelector(&preset.Spec.Selector)
		if err != nil {
			// Do not return an error from this function--or the VM will be
			// re-enqueued for processing again.
			recorder.Event(vm, k8sv1.EventTypeWarning, kubev1.PresetFailed.String(), fmt.Sprintf("Invalid Preset '%s': %v", preset.Name, err))
			logger.Object(&preset).Reason(err).Errorf("label selector conversion failed: %v", err)
		} else if selector.Matches(labels.Set(vm.GetLabels())) {
			logger.Object(vm).Infof("VirtualMachinePreset %s matches VirtualMachine", preset.GetName())
			matchingPresets = append(matchingPresets, preset)
		}
	}
	return matchingPresets
}

func checkPresetMergeConflicts(presetSpec *kubev1.DomainSpec, vmSpec *kubev1.DomainSpec) error {
	errors := []error{}
	if len(presetSpec.Resources.Requests) > 0 {
		for key, presetReq := range presetSpec.Resources.Requests {
			if vmReq, ok := vmSpec.Resources.Requests[key]; ok {
				if presetReq != vmReq {
					errors = append(errors, fmt.Errorf("spec.resources.requests[%s]: %v != %v", key, presetReq, vmReq))
				}
			}
		}
	}
	if presetSpec.CPU != nil && vmSpec.CPU != nil {
		if !reflect.DeepEqual(presetSpec.CPU, vmSpec.CPU) {
			errors = append(errors, fmt.Errorf("spec.cpu: %v != %v", presetSpec.CPU, vmSpec.CPU))
		}
	}
	if presetSpec.Firmware != nil && vmSpec.Firmware != nil {
		if !reflect.DeepEqual(presetSpec.Firmware, vmSpec.Firmware) {
			errors = append(errors, fmt.Errorf("spec.firmware: %v != %v", presetSpec.Firmware, vmSpec.Firmware))
		}
	}
	if presetSpec.Clock != nil && vmSpec.Clock != nil {
		if !reflect.DeepEqual(presetSpec.Clock.ClockOffset, vmSpec.Clock.ClockOffset) {
			errors = append(errors, fmt.Errorf("spec.clock.clockoffset: %v != %v", presetSpec.Clock.ClockOffset, vmSpec.Clock.ClockOffset))
		}
		if presetSpec.Clock.Timer != nil && vmSpec.Clock.Timer != nil {
			if !reflect.DeepEqual(presetSpec.Clock.Timer, vmSpec.Clock.Timer) {
				errors = append(errors, fmt.Errorf("spec.clock.timer: %v != %v", presetSpec.Clock.Timer, vmSpec.Clock.Timer))
			}
		}
	}
	if presetSpec.Features != nil && vmSpec.Features != nil {
		if !reflect.DeepEqual(presetSpec.Features, vmSpec.Features) {
			errors = append(errors, fmt.Errorf("spec.features: %v != %v", presetSpec.Features, vmSpec.Features))
		}
	}
	if presetSpec.Devices.Watchdog != nil && vmSpec.Devices.Watchdog != nil {
		if !reflect.DeepEqual(presetSpec.Devices.Watchdog, vmSpec.Devices.Watchdog) {
			errors = append(errors, fmt.Errorf("spec.devices.watchdog: %v != %v", presetSpec.Devices.Watchdog, vmSpec.Devices.Watchdog))
		}
	}

	if len(errors) > 0 {
		return utilerrors.NewAggregate(errors)
	}
	return nil
}

func mergeDomainSpec(presetSpec *kubev1.DomainSpec, vmSpec *kubev1.DomainSpec) (bool, error) {
	presetConflicts := checkPresetMergeConflicts(presetSpec, vmSpec)
	applied := false

	if len(presetSpec.Resources.Requests) > 0 {
		if vmSpec.Resources.Requests == nil {
			vmSpec.Resources.Requests = k8sv1.ResourceList{}
			for key, val := range presetSpec.Resources.Requests {
				vmSpec.Resources.Requests[key] = val
			}
			applied = true
		}
	}
	if presetSpec.CPU != nil {
		if vmSpec.CPU == nil {
			vmSpec.CPU = &kubev1.CPU{}
			presetSpec.CPU.DeepCopyInto(vmSpec.CPU)
			applied = true
		}
	}
	if presetSpec.Firmware != nil {
		if vmSpec.Firmware == nil {
			vmSpec.Firmware = &kubev1.Firmware{}
			presetSpec.Firmware.DeepCopyInto(vmSpec.Firmware)
			applied = true
		}
	}
	if presetSpec.Clock != nil {
		if vmSpec.Clock == nil {
			vmSpec.Clock = &kubev1.Clock{}
			vmSpec.Clock.ClockOffset = presetSpec.Clock.ClockOffset
			applied = true
		}

		if presetSpec.Clock.Timer != nil {
			if vmSpec.Clock.Timer == nil {
				vmSpec.Clock.Timer = &kubev1.Timer{}
				presetSpec.Clock.Timer.DeepCopyInto(vmSpec.Clock.Timer)
				applied = true
			}
		}
	}
	if presetSpec.Features != nil {
		if vmSpec.Features == nil {
			vmSpec.Features = &kubev1.Features{}
			presetSpec.Features.DeepCopyInto(vmSpec.Features)
			applied = true
		}
	}
	if presetSpec.Devices.Watchdog != nil {
		if vmSpec.Devices.Watchdog == nil {
			vmSpec.Devices.Watchdog = &kubev1.Watchdog{}
			presetSpec.Devices.Watchdog.DeepCopyInto(vmSpec.Devices.Watchdog)
			applied = true
		}
	}
	if presetConflicts != nil {
		return applied, presetConflicts
	}
	return applied, nil
}

func applyPresets(vm *kubev1.VirtualMachine, presets []kubev1.VirtualMachinePreset, recorder record.EventRecorder) {
	logger := log.Log
	for _, preset := range presets {
		applied, err := mergeDomainSpec(preset.Spec.Domain, &vm.Spec.Domain)
		if err != nil {
			recorder.Event(vm, k8sv1.EventTypeWarning, kubev1.PresetFailed.String(), fmt.Sprintf("Unable to apply Preset '%s': %v", preset.Name, err))
			logger.Object(vm).Errorf("Unable to apply Preset '%s': %v", preset.Name, err)
		}
		if applied {
			annotateVM(vm, preset)
		}
	}
}

// isInitialized checks if *this* module has initialized the VM,
// which is distinct from "has the VM been initialized by all controllers?"
func isInitialized(vm *kubev1.VirtualMachine) bool {
	// if initializers is nil/empty then consider this resource as initialized
	if vm.Initializers != nil && len(vm.Initializers.Pending) > 0 {
		for _, i := range vm.Initializers.Pending {
			if i.Name == initializerMarking {
				return false
			}
		}
	}
	return true
}

func removeInitializer(vm *kubev1.VirtualMachine) {
	if vm.Initializers == nil {
		// If Initializers is nil, there's nothing to remove.
		return
	}
	newInitilizers := []k8smetav1.Initializer{}
	for _, i := range vm.Initializers.Pending {
		if i.Name != initializerMarking {
			newInitilizers = append(newInitilizers, i)
		}
	}
	vm.Initializers.Pending = newInitilizers
}

func annotateVM(vm *kubev1.VirtualMachine, preset kubev1.VirtualMachinePreset) {
	if vm.Annotations == nil {
		vm.Annotations = map[string]string{}
	}
	annotationKey := fmt.Sprintf("virtualmachinepreset.%s/%s", kubev1.GroupName, preset.Name)
	vm.Annotations[annotationKey] = kubev1.GroupVersion.String()
}
<|endoftext|>


<commit_before>package sub

import (
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/objectcache"
	"github.com/Symantec/Dominator/lib/triggers"
	"github.com/Symantec/Dominator/proto/common"
	"time"
)

type Configuration struct {
	ScanSpeedPercent    uint
	NetworkSpeedPercent uint
	ScanExclusionList   []string
}

type FetchRequest struct {
	ServerAddress string
	Hashes        []hash.Hash
}

type FetchResponse common.StatusResponse

type GetConfigurationRequest struct {
}

type GetConfigurationResponse Configuration

// The GetFiles() RPC is fully streamed.
// The client sends a stream of strings (filenames) it wants. An empty string
// signals the end of the stream.
// The server (the sub) sends a stream of GetFileResponse messages. No response
// is sent for the end-of-stream signal.

type GetFileResponse struct {
	Error error
	Size  uint64
}

type PollRequest struct {
	HaveGeneration uint64
	ShortPollOnly  bool // If true, do not send FileSystem or ObjectCache.
}

type PollResponse struct {
	NetworkSpeed                 uint64
	FetchInProgress              bool // Fetch() and Update() mutually exclusive
	UpdateInProgress             bool
	LastFetchError               string
	LastUpdateError              string
	LastUpdateHadTriggerFailures bool
	StartTime                    time.Time
	PollTime                     time.Time
	GenerationCount              uint64
	FileSystem                   *filesystem.FileSystem // Streamed separately.
	FileSystemFollows            bool
	ObjectCache                  objectcache.ObjectCache // Streamed separately.
} // FileSystem is encoded afterwards, followed by ObjectCache.

type SetConfigurationRequest Configuration

type SetConfigurationResponse common.StatusResponse

type FileToCopyToCache struct {
	Name string
	Hash hash.Hash
}

type Hardlink struct {
	NewLink string
	Target  string
}

type Inode struct {
	Name string
	filesystem.GenericInode
}

type UpdateRequest struct {
	// The ordering here reflects the ordering that the sub is expected to use.
	FilesToCopyToCache  []FileToCopyToCache
	DirectoriesToMake   []Inode
	InodesToMake        []Inode
	HardlinksToMake     []Hardlink
	PathsToDelete       []string
	InodesToChange      []Inode
	MultiplyUsedObjects map[hash.Hash]uint64
	Triggers            *triggers.Triggers
}

type UpdateResponse struct{}

type CleanupRequest struct {
	Hashes []hash.Hash
}

type CleanupResponse struct{}
<commit_msg>Add ScanCount field to sub.PollResponse message.<commit_after>package sub

import (
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/objectcache"
	"github.com/Symantec/Dominator/lib/triggers"
	"github.com/Symantec/Dominator/proto/common"
	"time"
)

type Configuration struct {
	ScanSpeedPercent    uint
	NetworkSpeedPercent uint
	ScanExclusionList   []string
}

type FetchRequest struct {
	ServerAddress string
	Hashes        []hash.Hash
}

type FetchResponse common.StatusResponse

type GetConfigurationRequest struct {
}

type GetConfigurationResponse Configuration

// The GetFiles() RPC is fully streamed.
// The client sends a stream of strings (filenames) it wants. An empty string
// signals the end of the stream.
// The server (the sub) sends a stream of GetFileResponse messages. No response
// is sent for the end-of-stream signal.

type GetFileResponse struct {
	Error error
	Size  uint64
}

type PollRequest struct {
	HaveGeneration uint64
	ShortPollOnly  bool // If true, do not send FileSystem or ObjectCache.
}

type PollResponse struct {
	NetworkSpeed                 uint64
	FetchInProgress              bool // Fetch() and Update() mutually exclusive
	UpdateInProgress             bool
	LastFetchError               string
	LastUpdateError              string
	LastUpdateHadTriggerFailures bool
	StartTime                    time.Time
	PollTime                     time.Time
	ScanCount                    uint64
	GenerationCount              uint64
	FileSystem                   *filesystem.FileSystem // Streamed separately.
	FileSystemFollows            bool
	ObjectCache                  objectcache.ObjectCache // Streamed separately.
} // FileSystem is encoded afterwards, followed by ObjectCache.

type SetConfigurationRequest Configuration

type SetConfigurationResponse common.StatusResponse

type FileToCopyToCache struct {
	Name string
	Hash hash.Hash
}

type Hardlink struct {
	NewLink string
	Target  string
}

type Inode struct {
	Name string
	filesystem.GenericInode
}

type UpdateRequest struct {
	// The ordering here reflects the ordering that the sub is expected to use.
	FilesToCopyToCache  []FileToCopyToCache
	DirectoriesToMake   []Inode
	InodesToMake        []Inode
	HardlinksToMake     []Hardlink
	PathsToDelete       []string
	InodesToChange      []Inode
	MultiplyUsedObjects map[hash.Hash]uint64
	Triggers            *triggers.Triggers
}

type UpdateResponse struct{}

type CleanupRequest struct {
	Hashes []hash.Hash
}

type CleanupResponse struct{}
<|endoftext|>


<commit_before>package micro

import (
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro/broker"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/cmd"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/selector"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"

	"golang.org/x/net/context"
)

type Options struct {
	Broker    broker.Broker
	Cmd       cmd.Cmd
	Client    client.Client
	Server    server.Server
	Registry  registry.Registry
	Transport transport.Transport

	// Register loop interval
	RegisterInterval time.Duration

	// Before and After funcs
	BeforeStart []func() error
	BeforeStop  []func() error
	AfterStart  []func() error
	AfterStop   []func() error

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

func newOptions(opts ...Option) Options {
	opt := Options{
		Broker:    broker.DefaultBroker,
		Cmd:       cmd.DefaultCmd,
		Client:    client.DefaultClient,
		Server:    server.DefaultServer,
		Registry:  registry.DefaultRegistry,
		Transport: transport.DefaultTransport,
		Context:   context.Background(),
	}

	for _, o := range opts {
		o(&opt)
	}

	return opt
}

func Broker(b broker.Broker) Option {
	return func(o *Options) {
		o.Broker = b
		// Update Client and Server
		o.Client.Init(client.Broker(b))
		o.Server.Init(server.Broker(b))
	}
}

func Cmd(c cmd.Cmd) Option {
	return func(o *Options) {
		o.Cmd = c
	}
}

func Client(c client.Client) Option {
	return func(o *Options) {
		o.Client = c
	}
}

// Context specifies a context for the service.
// Can be used to signal shutdown of the service.
// Can be used for extra option values.
func Context(ctx context.Context) Option {
	return func(o *Options) {
		o.Context = ctx
	}
}

func Server(s server.Server) Option {
	return func(o *Options) {
		o.Server = s
	}
}

// Registry sets the registry for the service
// and the underlying components
func Registry(r registry.Registry) Option {
	return func(o *Options) {
		o.Registry = r
		// Update Client and Server
		o.Client.Init(client.Registry(r))
		o.Server.Init(server.Registry(r))
		// Update Selector
		o.Client.Options().Selector.Init(selector.Registry(r))
	}
}

// Transport sets the transport for the service
// and the underlying components
func Transport(t transport.Transport) Option {
	return func(o *Options) {
		o.Transport = t
		// Update Client and Server
		o.Client.Init(client.Transport(t))
		o.Server.Init(server.Transport(t))
	}
}

// Convenience options

// Name of the service
func Name(n string) Option {
	return func(o *Options) {
		o.Server.Init(server.Name(n))
	}
}

// Version of the service
func Version(v string) Option {
	return func(o *Options) {
		o.Server.Init(server.Version(v))
	}
}

// Metadata associated with the service
func Metadata(md map[string]string) Option {
	return func(o *Options) {
		o.Server.Init(server.Metadata(md))
	}
}

func Flags(flags ...cli.Flag) Option {
	return func(o *Options) {
		o.Cmd.App().Flags = append(o.Cmd.App().Flags, flags...)
	}
}

func Action(a func(*cli.Context)) Option {
	return func(o *Options) {
		o.Cmd.App().Action = a
	}
}

// RegisterTTL specifies the TTL to use when registering the service
func RegisterTTL(t time.Duration) Option {
	return func(o *Options) {
		o.Server.Init(server.RegisterTTL(t))
	}
}

// RegisterInterval specifies the interval on which to re-register
func RegisterInterval(t time.Duration) Option {
	return func(o *Options) {
		o.RegisterInterval = t
	}
}

// WrapClient is a convenience method for wrapping a Client with
// some middleware component. A list of wrappers can be provided.
func WrapClient(w ...client.Wrapper) Option {
	return func(o *Options) {
		// apply in reverse
		for i := len(w); i > 0; i-- {
			o.Client = w[i-1](o.Client)
		}
	}
}

// WrapCall is a convenience method for wrapping a Client CallFunc
func WrapCall(w ...client.CallWrapper) Option {
	return func(o *Options) {
		o.Client.Init(client.WrapCall(w...))
	}
}

// WrapHandler adds a handler Wrapper to a list of options passed into the server
func WrapHandler(w ...server.HandlerWrapper) Option {
	return func(o *Options) {
		var wrappers []server.Option

		for _, wrap := range w {
			wrappers = append(wrappers, server.WrapHandler(wrap))
		}

		// Init once
		o.Server.Init(wrappers...)
	}
}

// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the server
func WrapSubscriber(w ...server.SubscriberWrapper) Option {
	return func(o *Options) {
		var wrappers []server.Option

		for _, wrap := range w {
			wrappers = append(wrappers, server.WrapSubscriber(wrap))
		}

		// Init once
		o.Server.Init(wrappers...)
	}
}

// Before and Afters

func BeforeStart(fn func() error) Option {
	return func(o *Options) {
		o.BeforeStart = append(o.BeforeStart, fn)
	}
}

func BeforeStop(fn func() error) Option {
	return func(o *Options) {
		o.BeforeStop = append(o.BeforeStop, fn)
	}
}

func AfterStart(fn func() error) Option {
	return func(o *Options) {
		o.AfterStart = append(o.AfterStart, fn)
	}
}

func AfterStop(fn func() error) Option {
	return func(o *Options) {
		o.AfterStop = append(o.AfterStop, fn)
	}
}
<commit_msg>add option to set selector<commit_after>package micro

import (
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro/broker"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/cmd"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/selector"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"

	"golang.org/x/net/context"
)

type Options struct {
	Broker    broker.Broker
	Cmd       cmd.Cmd
	Client    client.Client
	Server    server.Server
	Registry  registry.Registry
	Transport transport.Transport

	// Register loop interval
	RegisterInterval time.Duration

	// Before and After funcs
	BeforeStart []func() error
	BeforeStop  []func() error
	AfterStart  []func() error
	AfterStop   []func() error

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

func newOptions(opts ...Option) Options {
	opt := Options{
		Broker:    broker.DefaultBroker,
		Cmd:       cmd.DefaultCmd,
		Client:    client.DefaultClient,
		Server:    server.DefaultServer,
		Registry:  registry.DefaultRegistry,
		Transport: transport.DefaultTransport,
		Context:   context.Background(),
	}

	for _, o := range opts {
		o(&opt)
	}

	return opt
}

func Broker(b broker.Broker) Option {
	return func(o *Options) {
		o.Broker = b
		// Update Client and Server
		o.Client.Init(client.Broker(b))
		o.Server.Init(server.Broker(b))
	}
}

func Cmd(c cmd.Cmd) Option {
	return func(o *Options) {
		o.Cmd = c
	}
}

func Client(c client.Client) Option {
	return func(o *Options) {
		o.Client = c
	}
}

// Context specifies a context for the service.
// Can be used to signal shutdown of the service.
// Can be used for extra option values.
func Context(ctx context.Context) Option {
	return func(o *Options) {
		o.Context = ctx
	}
}

func Server(s server.Server) Option {
	return func(o *Options) {
		o.Server = s
	}
}

// Registry sets the registry for the service
// and the underlying components
func Registry(r registry.Registry) Option {
	return func(o *Options) {
		o.Registry = r
		// Update Client and Server
		o.Client.Init(client.Registry(r))
		o.Server.Init(server.Registry(r))
		// Update Selector
		o.Client.Options().Selector.Init(selector.Registry(r))
	}
}

// Selector sets the selector for the service client
func Selector(s selector.Selector) Option {
	return func(o *Options) {
		o.Client.Init(client.Selector(s))
	}
}

// Transport sets the transport for the service
// and the underlying components
func Transport(t transport.Transport) Option {
	return func(o *Options) {
		o.Transport = t
		// Update Client and Server
		o.Client.Init(client.Transport(t))
		o.Server.Init(server.Transport(t))
	}
}

// Convenience options

// Name of the service
func Name(n string) Option {
	return func(o *Options) {
		o.Server.Init(server.Name(n))
	}
}

// Version of the service
func Version(v string) Option {
	return func(o *Options) {
		o.Server.Init(server.Version(v))
	}
}

// Metadata associated with the service
func Metadata(md map[string]string) Option {
	return func(o *Options) {
		o.Server.Init(server.Metadata(md))
	}
}

func Flags(flags ...cli.Flag) Option {
	return func(o *Options) {
		o.Cmd.App().Flags = append(o.Cmd.App().Flags, flags...)
	}
}

func Action(a func(*cli.Context)) Option {
	return func(o *Options) {
		o.Cmd.App().Action = a
	}
}

// RegisterTTL specifies the TTL to use when registering the service
func RegisterTTL(t time.Duration) Option {
	return func(o *Options) {
		o.Server.Init(server.RegisterTTL(t))
	}
}

// RegisterInterval specifies the interval on which to re-register
func RegisterInterval(t time.Duration) Option {
	return func(o *Options) {
		o.RegisterInterval = t
	}
}

// WrapClient is a convenience method for wrapping a Client with
// some middleware component. A list of wrappers can be provided.
func WrapClient(w ...client.Wrapper) Option {
	return func(o *Options) {
		// apply in reverse
		for i := len(w); i > 0; i-- {
			o.Client = w[i-1](o.Client)
		}
	}
}

// WrapCall is a convenience method for wrapping a Client CallFunc
func WrapCall(w ...client.CallWrapper) Option {
	return func(o *Options) {
		o.Client.Init(client.WrapCall(w...))
	}
}

// WrapHandler adds a handler Wrapper to a list of options passed into the server
func WrapHandler(w ...server.HandlerWrapper) Option {
	return func(o *Options) {
		var wrappers []server.Option

		for _, wrap := range w {
			wrappers = append(wrappers, server.WrapHandler(wrap))
		}

		// Init once
		o.Server.Init(wrappers...)
	}
}

// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the server
func WrapSubscriber(w ...server.SubscriberWrapper) Option {
	return func(o *Options) {
		var wrappers []server.Option

		for _, wrap := range w {
			wrappers = append(wrappers, server.WrapSubscriber(wrap))
		}

		// Init once
		o.Server.Init(wrappers...)
	}
}

// Before and Afters

func BeforeStart(fn func() error) Option {
	return func(o *Options) {
		o.BeforeStart = append(o.BeforeStart, fn)
	}
}

func BeforeStop(fn func() error) Option {
	return func(o *Options) {
		o.BeforeStop = append(o.BeforeStop, fn)
	}
}

func AfterStart(fn func() error) Option {
	return func(o *Options) {
		o.AfterStart = append(o.AfterStart, fn)
	}
}

func AfterStop(fn func() error) Option {
	return func(o *Options) {
		o.AfterStop = append(o.AfterStop, fn)
	}
}
<|endoftext|>


<commit_before>package fs

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/alcortesm/tgz"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type FSImplSuite struct {
	dir string
}

var _ = Suite(&FSImplSuite{})

func (s *FSImplSuite) SetUpSuite(c *C) {
	dir, err := tgz.Extract("../../storage/filesystem/internal/gitdir/fixtures/spinnaker-gc.tgz")
	c.Assert(err, IsNil)
	s.dir = dir
}

func (s *FSImplSuite) TearDownSuite(c *C) {
	err := os.RemoveAll(s.dir)
	c.Assert(err, IsNil)
}

func (s *FSImplSuite) TestJoin(c *C) {
	fs := NewOS()
	for i, test := range [...]struct {
		input    []string
		expected string
	}{
		{
			input:    []string{},
			expected: "",
		}, {
			input:    []string{"a"},
			expected: "a",
		}, {
			input:    []string{"a", "b"},
			expected: "a/b",
		}, {
			input:    []string{"a", "b", "c"},
			expected: "a/b/c",
		},
	} {
		obtained := fs.Join(test.input...)
		com := Commentf("test %d:\n\tinput = %v", i, test.input)
		c.Assert(obtained, Equals, test.expected, com)
	}
}

func (s *FSImplSuite) TestStat(c *C) {
	fs := NewOS()
	for i, path := range [...]string{
		".git/index",
		".git/info/refs",
		".git/objects/pack/pack-584416f86235cac0d54bfabbdc399fb2b09a5269.pack",
	} {
		path := fs.Join(s.dir, path)
		com := Commentf("test %d", i)

		real, err := os.Open(path)
		c.Assert(err, IsNil, com)

		expected, err := real.Stat()
		c.Assert(err, IsNil, com)

		obtained, err := fs.Stat(path)
		c.Assert(err, IsNil, com)

		c.Assert(obtained, DeepEquals, expected, com)

		err = real.Close()
		c.Assert(err, IsNil, com)
	}
}

func (s *FSImplSuite) TestStatErrors(c *C) {
	fs := NewOS()
	for i, test := range [...]struct {
		input     string
		errRegExp string
	}{
		{
			input:     "bla",
			errRegExp: ".*bla: no such file or directory",
		}, {
			input:     "bla/foo",
			errRegExp: ".*bla/foo: no such file or directory",
		},
	} {
		com := Commentf("test %d", i)
		_, err := fs.Stat(test.input)
		c.Assert(err, ErrorMatches, test.errRegExp, com)
	}
}

func (s *FSImplSuite) TestOpen(c *C) {
	fs := NewOS()
	for i, test := range [...]string{
		".git/index",
		".git/info/refs",
		".git/objects/pack/pack-584416f86235cac0d54bfabbdc399fb2b09a5269.pack",
	} {
		com := Commentf("test %d", i)
		path := fs.Join(s.dir, test)

		real, err := os.Open(path)
		c.Assert(err, IsNil, com)
		realData, err := ioutil.ReadAll(real)
		c.Assert(err, IsNil, com)
		err = real.Close()
		c.Assert(err, IsNil, com)

		obtained, err := fs.Open(path)
		c.Assert(err, IsNil, com)
		obtainedData, err := ioutil.ReadAll(obtained)
		c.Assert(err, IsNil, com)
		err = obtained.Close()
		c.Assert(err, IsNil, com)

		c.Assert(obtainedData, DeepEquals, realData, com)
	}
}

func (s *FSImplSuite) TestReadDir(c *C) {
	fs := NewOS()
	for i, test := range [...]string{
		".git/info",
		".",
		"",
		".git/objects",
		".git/objects/pack",
	} {
		com := Commentf("test %d", i)
		path := fs.Join(s.dir, test)

		expected, err := ioutil.ReadDir(path)
		c.Assert(err, IsNil, com)

		obtained, err := fs.ReadDir(path)
		c.Assert(err, IsNil, com)

		c.Assert(obtained, DeepEquals, expected, com)
	}
}
<commit_msg>fix tests and examples<commit_after>package fs

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/alcortesm/tgz"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type FSImplSuite struct {
	dir string
}

var _ = Suite(&FSImplSuite{})

func (s *FSImplSuite) SetUpSuite(c *C) {
	dir, err := tgz.Extract("../../storage/filesystem/internal/dotgit/fixtures/spinnaker-gc.tgz")
	c.Assert(err, IsNil)
	s.dir = dir
}

func (s *FSImplSuite) TearDownSuite(c *C) {
	err := os.RemoveAll(s.dir)
	c.Assert(err, IsNil)
}

func (s *FSImplSuite) TestJoin(c *C) {
	fs := NewOS()
	for i, test := range [...]struct {
		input    []string
		expected string
	}{
		{
			input:    []string{},
			expected: "",
		}, {
			input:    []string{"a"},
			expected: "a",
		}, {
			input:    []string{"a", "b"},
			expected: "a/b",
		}, {
			input:    []string{"a", "b", "c"},
			expected: "a/b/c",
		},
	} {
		obtained := fs.Join(test.input...)
		com := Commentf("test %d:\n\tinput = %v", i, test.input)
		c.Assert(obtained, Equals, test.expected, com)
	}
}

func (s *FSImplSuite) TestStat(c *C) {
	fs := NewOS()
	for i, path := range [...]string{
		".git/index",
		".git/info/refs",
		".git/objects/pack/pack-584416f86235cac0d54bfabbdc399fb2b09a5269.pack",
	} {
		path := fs.Join(s.dir, path)
		com := Commentf("test %d", i)

		real, err := os.Open(path)
		c.Assert(err, IsNil, com)

		expected, err := real.Stat()
		c.Assert(err, IsNil, com)

		obtained, err := fs.Stat(path)
		c.Assert(err, IsNil, com)

		c.Assert(obtained, DeepEquals, expected, com)

		err = real.Close()
		c.Assert(err, IsNil, com)
	}
}

func (s *FSImplSuite) TestStatErrors(c *C) {
	fs := NewOS()
	for i, test := range [...]struct {
		input     string
		errRegExp string
	}{
		{
			input:     "bla",
			errRegExp: ".*bla: no such file or directory",
		}, {
			input:     "bla/foo",
			errRegExp: ".*bla/foo: no such file or directory",
		},
	} {
		com := Commentf("test %d", i)
		_, err := fs.Stat(test.input)
		c.Assert(err, ErrorMatches, test.errRegExp, com)
	}
}

func (s *FSImplSuite) TestOpen(c *C) {
	fs := NewOS()
	for i, test := range [...]string{
		".git/index",
		".git/info/refs",
		".git/objects/pack/pack-584416f86235cac0d54bfabbdc399fb2b09a5269.pack",
	} {
		com := Commentf("test %d", i)
		path := fs.Join(s.dir, test)

		real, err := os.Open(path)
		c.Assert(err, IsNil, com)
		realData, err := ioutil.ReadAll(real)
		c.Assert(err, IsNil, com)
		err = real.Close()
		c.Assert(err, IsNil, com)

		obtained, err := fs.Open(path)
		c.Assert(err, IsNil, com)
		obtainedData, err := ioutil.ReadAll(obtained)
		c.Assert(err, IsNil, com)
		err = obtained.Close()
		c.Assert(err, IsNil, com)

		c.Assert(obtainedData, DeepEquals, realData, com)
	}
}

func (s *FSImplSuite) TestReadDir(c *C) {
	fs := NewOS()
	for i, test := range [...]string{
		".git/info",
		".",
		"",
		".git/objects",
		".git/objects/pack",
	} {
		com := Commentf("test %d", i)
		path := fs.Join(s.dir, test)

		expected, err := ioutil.ReadDir(path)
		c.Assert(err, IsNil, com)

		obtained, err := fs.ReadDir(path)
		c.Assert(err, IsNil, com)

		c.Assert(obtained, DeepEquals, expected, com)
	}
}
<|endoftext|>