<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"context"
	"os"
	"os/user"
	"path/filepath"

	"cloud.google.com/go/storage"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"k8s.io/release/pkg/release"
	"k8s.io/release/pkg/util"
)

const description = `
Used for pushing developer builds and Jenkins' continuous builds.

Developer pushes simply run as they do pushing to devel/ on GCS.
In --ci mode, 'push' runs in mock mode by default. Use --nomock to do
a real push.

push                       - Do a developer push
push --nomock --ci
                           - Do a (non-mocked) CI push
push --bucket=kubernetes-release-$USER
                           - Do a developer push to kubernetes-release-$USER`

type pushBuildOptions struct {
	bucket           string
	buildDir         string
	dockerRegistry   string
	extraPublishFile string
	gcsSuffix        string
	releaseType      string
	versionSuffix    string
	allowDup         bool
	ci               bool
	noUpdateLatest   bool
	privateBucket    bool
}

var pushBuildOpts = &pushBuildOptions{}

var pushBuildCmd = &cobra.Command{
	Use:           "push [--noupdatelatest] [--ci] [--bucket=<GS bucket>] [--private-bucket]",
	Short:         "Push Kubernetes release artifacts to Google Cloud Storage (GCS)",
	Example:       description,
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		if err := runPushBuild(pushBuildOpts); err != nil {
			return err
		}

		return nil
	},
}

type stageFile struct {
	srcPath  string
	dstPath  string
	required bool
}

var gcpStageFiles = []stageFile{
	{
		srcPath:  filepath.Join(release.GCEPath, "configure-vm.sh"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: false,
	},
	{
		srcPath:  filepath.Join(release.GCIPath, "node.yaml"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: true,
	},
	{
		srcPath:  filepath.Join(release.GCIPath, "master.yaml"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: true,
	},
	{
		srcPath:  filepath.Join(release.GCIPath, "configure.sh"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: true,
	},
	{
		srcPath:  filepath.Join(release.GCIPath, "shutdown.sh"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: false,
	},
}

var windowsStageFiles = []stageFile{
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "configure.ps1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "common.psm1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "k8s-node-setup.psm1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "testonly/install-ssh.psm1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "testonly/user-profile.psm1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
}

func init() {
	pushBuildCmd.PersistentFlags().BoolVar(
		&pushBuildOpts.allowDup,
		"allow-dup",
		false,
		"Do not exit error if the build already exists on the gcs path",
	)
	pushBuildCmd.PersistentFlags().BoolVar(
		&pushBuildOpts.ci,
		"ci",
		false,
		"Used when called from Jenkins (for ci runs)",
	)
	pushBuildCmd.PersistentFlags().BoolVar(
		&pushBuildOpts.noUpdateLatest,
		"noupdatelatest",
		false,
		"Do not update the latest file",
	)
	pushBuildCmd.PersistentFlags().BoolVar(
		&pushBuildOpts.privateBucket,
		"private-bucket",
		false,
		"Do not mark published bits on GCS as publicly readable",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.bucket,
		"bucket",
		"devel",
		"Specify an alternate bucket for pushes (normally 'devel' or 'ci')",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.buildDir,
		"buildDir",
		"_output",
		"Specify an alternate build directory (defaults to '_output')",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.dockerRegistry,
		"docker-registry",
		"",
		"If set, push docker images to specified registry/project",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.extraPublishFile,
		"extra-publish-file",
		"",
		"Used when need to upload additional version file to GCS. The path is relative and is append to a GCS path. (--ci only)",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.gcsSuffix,
		"gcs-suffix",
		"",
		"Specify a suffix to append to the upload destination on GCS",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.releaseType,
		"release-type",
		"devel",
		"Specify an alternate bucket for pushes (normally 'devel' or 'ci')",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.versionSuffix,
		"version-suffix",
		"",
		"Append suffix to version name if set",
	)

	rootCmd.AddCommand(pushBuildCmd)
}

func runPushBuild(opts *pushBuildOptions) error {
	var latest string

	// Check if latest build uses bazel
	dir, err := os.Getwd()
	if err != nil {
		return errors.Wrap(err, "Unable to get working directory")
	}

	isBazel, err := release.BuiltWithBazel(dir)
	if err != nil {
		return errors.Wrap(err, "Unable to identify if release built with Bazel")
	}

	if isBazel {
		logrus.Info("Using Bazel build version")
		version, err := release.ReadBazelVersion(dir)
		if err != nil {
			return errors.Wrap(err, "Unable to read Bazel build version")
		}
		latest = version
	} else {
		logrus.Info("Using Dockerized build version")
		version, err := release.ReadDockerizedVersion(dir)
		if err != nil {
			return errors.Wrap(err, "Unable to read Dockerized build version")
		}
		latest = version
	}

	logrus.Infof("Found build version: %s", latest)

	valid, err := release.IsValidReleaseBuild(latest)
	if err != nil {
		return errors.Wrap(err, "Unable to determine if release build version is valid")
	}
	if !valid {
		return errors.Errorf("Build version %s is not valid for release", latest)
	}

	if opts.ci && release.IsDirtyBuild(latest) {
		return errors.New(`Refusing to push dirty build with --ci flag given.\n
			CI builds should always be performed from clean commits`)
	}

	if opts.versionSuffix != "" {
		latest += "-" + opts.versionSuffix
	}

	logrus.Infof("Latest version is %s", latest)

	gcsDest := opts.releaseType

	// TODO: is this how we want to handle gcs dest args?
	if opts.ci {
		gcsDest = "ci"
	}

	gcsDest += opts.gcsSuffix

	logrus.Infof("GCS destination is %s", gcsDest)

	releaseBucket := opts.bucket
	if rootOpts.nomock {
		logrus.Infof("Running a *REAL* push with bucket %s", releaseBucket)
	} else {
		u, err := user.Current()
		if err != nil {
			return errors.Wrap(err, "Unable to identify current user")
		}

		releaseBucket += "-" + u.Username
	}

	client, err := storage.NewClient(context.Background())
	if err != nil {
		return errors.Wrap(err, "error fetching gcloud credentials... try running \"gcloud auth application-default login\"")
	}

	bucket := client.Bucket(releaseBucket)
	if bucket == nil {
		return errors.Errorf("unable to identify specified bucket for artifacts: %s", releaseBucket)
	}

	// Check if bucket exists and user has permissions
	requiredGCSPerms := []string{"storage.objects.create"}
	perms, err := bucket.IAM().TestPermissions(context.Background(), requiredGCSPerms)
	if err != nil {
		return errors.Wrap(err, "Unable to find release artifact bucket")
	}
	if len(perms) != 1 {
		return errors.Errorf("GCP user must have at least %s permissions on bucket %s", requiredGCSPerms, releaseBucket)
	}

	buildDir := buildOpts.BuildDir
	if err = util.RemoveAndReplaceDir(filepath.Join(buildDir, release.GCSStagePath)); err != nil {
		return errors.Wrap(err, "Unable remove and replace GCS staging directory.")
	}

	// Copy release tarballs to local GCS staging directory for push
	if err = util.CopyDirContentsLocal(filepath.Join(buildDir, release.ReleaseTarsPath), filepath.Join(buildDir, release.GCSStagePath)); err != nil {
		return errors.Wrap(err, "Unable to copy source directory into destination")
	}

	// Copy helpful GCP scripts to local GCS staging directory for push
	for _, file := range gcpStageFiles {
		if err := util.CopyFileLocal(filepath.Join(buildDir, file.srcPath), filepath.Join(buildDir, file.dstPath), file.required); err != nil {
			return err
		}
	}

	// Copy helpful Windows scripts to local GCS staging directory for push
	for _, file := range windowsStageFiles {
		if err := util.CopyFileLocal(filepath.Join(buildDir, file.srcPath), filepath.Join(buildDir, file.dstPath), file.required); err != nil {
			return err
		}
	}

	// TODO
	// Prepare naked binaries
	// Write checksums
	// Push Docker images
	// Push artifacts to release bucket is --ci

	return nil
}
<commit_msg>Streamline krel push error messages<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"context"
	"os"
	"os/user"
	"path/filepath"

	"cloud.google.com/go/storage"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"k8s.io/release/pkg/release"
	"k8s.io/release/pkg/util"
)

const description = `
Used for pushing developer builds and Jenkins' continuous builds.

Developer pushes simply run as they do pushing to devel/ on GCS.
In --ci mode, 'push' runs in mock mode by default. Use --nomock to do
a real push.

push                       - Do a developer push
push --nomock --ci
                           - Do a (non-mocked) CI push
push --bucket=kubernetes-release-$USER
                           - Do a developer push to kubernetes-release-$USER`

type pushBuildOptions struct {
	bucket           string
	buildDir         string
	dockerRegistry   string
	extraPublishFile string
	gcsSuffix        string
	releaseType      string
	versionSuffix    string
	allowDup         bool
	ci               bool
	noUpdateLatest   bool
	privateBucket    bool
}

var pushBuildOpts = &pushBuildOptions{}

var pushBuildCmd = &cobra.Command{
	Use:           "push [--noupdatelatest] [--ci] [--bucket=<GS bucket>] [--private-bucket]",
	Short:         "Push Kubernetes release artifacts to Google Cloud Storage (GCS)",
	Example:       description,
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		if err := runPushBuild(pushBuildOpts); err != nil {
			return errors.Wrap(err, "Failed to run:")
		}

		return nil
	},
}

type stageFile struct {
	srcPath  string
	dstPath  string
	required bool
}

var gcpStageFiles = []stageFile{
	{
		srcPath:  filepath.Join(release.GCEPath, "configure-vm.sh"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: false,
	},
	{
		srcPath:  filepath.Join(release.GCIPath, "node.yaml"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: true,
	},
	{
		srcPath:  filepath.Join(release.GCIPath, "master.yaml"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: true,
	},
	{
		srcPath:  filepath.Join(release.GCIPath, "configure.sh"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: true,
	},
	{
		srcPath:  filepath.Join(release.GCIPath, "shutdown.sh"),
		dstPath:  filepath.Join(release.GCSStagePath, "extra/gce"),
		required: false,
	},
}

var windowsStageFiles = []stageFile{
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "configure.ps1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "common.psm1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "k8s-node-setup.psm1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "testonly/install-ssh.psm1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
	{
		srcPath:  filepath.Join(release.WindowsLocalPath, "testonly/user-profile.psm1"),
		dstPath:  release.WindowsGCSPath,
		required: true,
	},
}

func init() {
	pushBuildCmd.PersistentFlags().BoolVar(
		&pushBuildOpts.allowDup,
		"allow-dup",
		false,
		"Do not exit error if the build already exists on the gcs path",
	)
	pushBuildCmd.PersistentFlags().BoolVar(
		&pushBuildOpts.ci,
		"ci",
		false,
		"Used when called from Jenkins (for ci runs)",
	)
	pushBuildCmd.PersistentFlags().BoolVar(
		&pushBuildOpts.noUpdateLatest,
		"noupdatelatest",
		false,
		"Do not update the latest file",
	)
	pushBuildCmd.PersistentFlags().BoolVar(
		&pushBuildOpts.privateBucket,
		"private-bucket",
		false,
		"Do not mark published bits on GCS as publicly readable",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.bucket,
		"bucket",
		"devel",
		"Specify an alternate bucket for pushes (normally 'devel' or 'ci')",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.buildDir,
		"buildDir",
		"_output",
		"Specify an alternate build directory (defaults to '_output')",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.dockerRegistry,
		"docker-registry",
		"",
		"If set, push docker images to specified registry/project",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.extraPublishFile,
		"extra-publish-file",
		"",
		"Used when need to upload additional version file to GCS. The path is relative and is append to a GCS path. (--ci only)",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.gcsSuffix,
		"gcs-suffix",
		"",
		"Specify a suffix to append to the upload destination on GCS",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.releaseType,
		"release-type",
		"devel",
		"Specify an alternate bucket for pushes (normally 'devel' or 'ci')",
	)
	pushBuildCmd.PersistentFlags().StringVar(
		&pushBuildOpts.versionSuffix,
		"version-suffix",
		"",
		"Append suffix to version name if set",
	)

	rootCmd.AddCommand(pushBuildCmd)
}

func runPushBuild(opts *pushBuildOptions) error {
	var latest string

	// Check if latest build uses bazel
	dir, err := os.Getwd()
	if err != nil {
		return errors.Wrap(err, "get working directory")
	}

	isBazel, err := release.BuiltWithBazel(dir)
	if err != nil {
		return errors.Wrap(err, "identify if release built with Bazel")
	}

	if isBazel {
		logrus.Info("Using Bazel build version")
		version, err := release.ReadBazelVersion(dir)
		if err != nil {
			return errors.Wrap(err, "read Bazel build version")
		}
		latest = version
	} else {
		logrus.Info("Using Dockerized build version")
		version, err := release.ReadDockerizedVersion(dir)
		if err != nil {
			return errors.Wrap(err, "read Dockerized build version")
		}
		latest = version
	}

	logrus.Infof("Found build version: %s", latest)

	valid, err := release.IsValidReleaseBuild(latest)
	if err != nil {
		return errors.Wrap(err, "determine if release build version is valid")
	}
	if !valid {
		return errors.Errorf("build version %s is not valid for release", latest)
	}

	if opts.ci && release.IsDirtyBuild(latest) {
		return errors.New(`refusing to push dirty build with --ci flag given. CI builds should always be performed from clean commits`)
	}

	if opts.versionSuffix != "" {
		latest += "-" + opts.versionSuffix
	}

	logrus.Infof("Latest version is %s", latest)

	gcsDest := opts.releaseType

	// TODO: is this how we want to handle gcs dest args?
	if opts.ci {
		gcsDest = "ci"
	}

	gcsDest += opts.gcsSuffix

	logrus.Infof("GCS destination is %s", gcsDest)

	releaseBucket := opts.bucket
	if rootOpts.nomock {
		logrus.Infof("Running a *REAL* push with bucket %s", releaseBucket)
	} else {
		u, err := user.Current()
		if err != nil {
			return errors.Wrap(err, "identify current user")
		}

		releaseBucket += "-" + u.Username
	}

	client, err := storage.NewClient(context.Background())
	if err != nil {
		return errors.Wrap(err, "fetching gcloud credentials... try running \"gcloud auth application-default login\"")
	}

	bucket := client.Bucket(releaseBucket)
	if bucket == nil {
		return errors.Errorf("identify specified bucket for artifacts: %s", releaseBucket)
	}

	// Check if bucket exists and user has permissions
	requiredGCSPerms := []string{"storage.objects.create"}
	perms, err := bucket.IAM().TestPermissions(context.Background(), requiredGCSPerms)
	if err != nil {
		return errors.Wrap(err, "find release artifact bucket")
	}
	if len(perms) != 1 {
		return errors.Errorf("GCP user must have at least %s permissions on bucket %s", requiredGCSPerms, releaseBucket)
	}

	buildDir := buildOpts.BuildDir
	if err = util.RemoveAndReplaceDir(filepath.Join(buildDir, release.GCSStagePath)); err != nil {
		return errors.Wrap(err, "remove and replace GCS staging directory")
	}

	// Copy release tarballs to local GCS staging directory for push
	if err = util.CopyDirContentsLocal(filepath.Join(buildDir, release.ReleaseTarsPath), filepath.Join(buildDir, release.GCSStagePath)); err != nil {
		return errors.Wrap(err, "copy source directory into destination")
	}

	// Copy helpful GCP scripts to local GCS staging directory for push
	for _, file := range gcpStageFiles {
		if err := util.CopyFileLocal(filepath.Join(buildDir, file.srcPath), filepath.Join(buildDir, file.dstPath), file.required); err != nil {
			return errors.Wrap(err, "copy GCP stage files")
		}
	}

	// Copy helpful Windows scripts to local GCS staging directory for push
	for _, file := range windowsStageFiles {
		if err := util.CopyFileLocal(filepath.Join(buildDir, file.srcPath), filepath.Join(buildDir, file.dstPath), file.required); err != nil {
			return errors.Wrap(err, "copy Windows stage files")
		}
	}

	// TODO
	// Prepare naked binaries
	// Write checksums
	// Push Docker images
	// Push artifacts to release bucket is --ci

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rsa"
	"errors"
	goflag "flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"

	flag "github.com/spf13/pflag"
	"k8s.io/apimachinery/pkg/runtime"
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/cert"

	ssv1alpha1 "github.com/bitnami/sealed-secrets/apis/v1alpha1"

	// Register v1.Secret type
	_ "k8s.io/client-go/pkg/api/install"
)

var (
	// TODO: Verify k8s server signature against cert in kube client config.
	certFile       = flag.String("cert", "", "Certificate / public key to use for encryption. Overrides --controller-*")
	controllerNs   = flag.String("controller-namespace", api.NamespaceSystem, "Namespace of sealed-secrets controller.")
	controllerName = flag.String("controller-name", "sealed-secrets-controller", "Name of sealed-secrets controller.")

	clientConfig clientcmd.ClientConfig
)

func init() {
	// The "usual" clientcmd/kubectl flags
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
	overrides := clientcmd.ConfigOverrides{}
	kflags := clientcmd.RecommendedConfigOverrideFlags("")
	flag.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
	clientcmd.BindOverrideFlags(&overrides, flag.CommandLine, kflags)
	clientConfig = clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)

	// Standard goflags (glog in particular)
	flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}

func parseKey(r io.Reader) (*rsa.PublicKey, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	certs, err := cert.ParseCertsPEM(data)
	if err != nil {
		return nil, err
	}

	// ParseCertsPem returns error if len(certs) == 0, but best to be sure...
	if len(certs) == 0 {
		return nil, errors.New("Failed to read any certificates")
	}

	cert, ok := certs[0].PublicKey.(*rsa.PublicKey)
	if !ok {
		return nil, fmt.Errorf("Expected RSA public key but found %v", certs[0].PublicKey)
	}

	return cert, nil
}

func readSecret(codec runtime.Decoder, r io.Reader) (*v1.Secret, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	var ret v1.Secret
	if err = runtime.DecodeInto(codec, data, &ret); err != nil {
		return nil, err
	}

	return &ret, nil
}

func prettyEncoder(codecs runtimeserializer.CodecFactory, mediaType string, gv runtime.GroupVersioner) (runtime.Encoder, error) {
	info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
	if !ok {
		return nil, fmt.Errorf("binary can't serialize %s", mediaType)
	}

	prettyEncoder := info.PrettySerializer
	if prettyEncoder == nil {
		prettyEncoder = info.Serializer
	}

	enc := codecs.EncoderForVersion(prettyEncoder, gv)
	return enc, nil
}

func openCertFile(certFile string) (io.ReadCloser, error) {
	f, err := os.Open(certFile)
	if err != nil {
		return nil, fmt.Errorf("Error reading %s: %v", certFile, err)
	}
	return f, nil
}

func openCertHTTP(c corev1.CoreV1Interface, namespace, name string) (io.ReadCloser, error) {
	f, err := c.
		Services(namespace).
		ProxyGet("http", name, "", "/v1/cert.pem", nil).
		Stream()
	if err != nil {
		return nil, fmt.Errorf("Error fetching certificate: %v", err)
	}
	return f, nil
}

func openCert() (io.ReadCloser, error) {
	if *certFile != "" {
		return openCertFile(*certFile)
	}

	conf, err := clientConfig.ClientConfig()
	if err != nil {
		return nil, err
	}
	conf.AcceptContentTypes = "application/x-pem-file, */*"
	restClient, err := corev1.NewForConfig(conf)
	if err != nil {
		return nil, err
	}
	return openCertHTTP(restClient, *controllerNs, *controllerName)
}

func seal(in io.Reader, out io.Writer, codecs runtimeserializer.CodecFactory, pubKey *rsa.PublicKey) error {
	secret, err := readSecret(codecs.UniversalDecoder(), in)
	if err != nil {
		return err
	}

	if secret.GetNamespace() == "" {
		ns, _, err := clientConfig.Namespace()
		if err != nil {
			return err
		}
		secret.SetNamespace(ns)
	}

	ssecret, err := ssv1alpha1.NewSealedSecret(codecs, pubKey, secret)
	if err != nil {
		return err
	}

	prettyEnc, err := prettyEncoder(codecs, runtime.ContentTypeJSON, ssv1alpha1.SchemeGroupVersion)
	if err != nil {
		return err
	}

	buf, err := runtime.Encode(prettyEnc, ssecret)
	if err != nil {
		return err
	}

	out.Write(buf)
	fmt.Fprint(out, "\n")

	return nil
}

func main() {
	flag.Parse()
	goflag.CommandLine.Parse([]string{})

	f, err := openCert()
	if err != nil {
		panic(err.Error())
	}
	defer f.Close()

	pubKey, err := parseKey(f)
	if err != nil {
		panic(err.Error())
	}

	if err := seal(os.Stdin, os.Stdout, api.Codecs, pubKey); err != nil {
		panic(err.Error())
	}
}
<commit_msg>Add support for YAML output<commit_after>package main

import (
	"crypto/rsa"
	"errors"
	goflag "flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	flag "github.com/spf13/pflag"
	"k8s.io/apimachinery/pkg/runtime"
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/cert"

	ssv1alpha1 "github.com/bitnami/sealed-secrets/apis/v1alpha1"

	// Register v1.Secret type
	_ "k8s.io/client-go/pkg/api/install"
)

var (
	// TODO: Verify k8s server signature against cert in kube client config.
	certFile       = flag.String("cert", "", "Certificate / public key to use for encryption. Overrides --controller-*")
	controllerNs   = flag.String("controller-namespace", api.NamespaceSystem, "Namespace of sealed-secrets controller.")
	controllerName = flag.String("controller-name", "sealed-secrets-controller", "Name of sealed-secrets controller.")
	outputFormat   = flag.String("format", "json", "Output format for sealed secret. Either json or yaml")

	clientConfig clientcmd.ClientConfig
)

func init() {
	// The "usual" clientcmd/kubectl flags
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
	overrides := clientcmd.ConfigOverrides{}
	kflags := clientcmd.RecommendedConfigOverrideFlags("")
	flag.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
	clientcmd.BindOverrideFlags(&overrides, flag.CommandLine, kflags)
	clientConfig = clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)

	// Standard goflags (glog in particular)
	flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}

func parseKey(r io.Reader) (*rsa.PublicKey, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	certs, err := cert.ParseCertsPEM(data)
	if err != nil {
		return nil, err
	}

	// ParseCertsPem returns error if len(certs) == 0, but best to be sure...
	if len(certs) == 0 {
		return nil, errors.New("Failed to read any certificates")
	}

	cert, ok := certs[0].PublicKey.(*rsa.PublicKey)
	if !ok {
		return nil, fmt.Errorf("Expected RSA public key but found %v", certs[0].PublicKey)
	}

	return cert, nil
}

func readSecret(codec runtime.Decoder, r io.Reader) (*v1.Secret, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	var ret v1.Secret
	if err = runtime.DecodeInto(codec, data, &ret); err != nil {
		return nil, err
	}

	return &ret, nil
}

func prettyEncoder(codecs runtimeserializer.CodecFactory, mediaType string, gv runtime.GroupVersioner) (runtime.Encoder, error) {
	info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
	if !ok {
		return nil, fmt.Errorf("binary can't serialize %s", mediaType)
	}

	prettyEncoder := info.PrettySerializer
	if prettyEncoder == nil {
		prettyEncoder = info.Serializer
	}

	enc := codecs.EncoderForVersion(prettyEncoder, gv)
	return enc, nil
}

func openCertFile(certFile string) (io.ReadCloser, error) {
	f, err := os.Open(certFile)
	if err != nil {
		return nil, fmt.Errorf("Error reading %s: %v", certFile, err)
	}
	return f, nil
}

func openCertHTTP(c corev1.CoreV1Interface, namespace, name string) (io.ReadCloser, error) {
	f, err := c.
		Services(namespace).
		ProxyGet("http", name, "", "/v1/cert.pem", nil).
		Stream()
	if err != nil {
		return nil, fmt.Errorf("Error fetching certificate: %v", err)
	}
	return f, nil
}

func openCert() (io.ReadCloser, error) {
	if *certFile != "" {
		return openCertFile(*certFile)
	}

	conf, err := clientConfig.ClientConfig()
	if err != nil {
		return nil, err
	}
	conf.AcceptContentTypes = "application/x-pem-file, */*"
	restClient, err := corev1.NewForConfig(conf)
	if err != nil {
		return nil, err
	}
	return openCertHTTP(restClient, *controllerNs, *controllerName)
}

func seal(in io.Reader, out io.Writer, codecs runtimeserializer.CodecFactory, pubKey *rsa.PublicKey) error {
	secret, err := readSecret(codecs.UniversalDecoder(), in)
	if err != nil {
		return err
	}

	if secret.GetNamespace() == "" {
		ns, _, err := clientConfig.Namespace()
		if err != nil {
			return err
		}
		secret.SetNamespace(ns)
	}

	ssecret, err := ssv1alpha1.NewSealedSecret(codecs, pubKey, secret)
	if err != nil {
		return err
	}

	var contentType string
	switch strings.ToLower(*outputFormat) {
	case "json", "":
		contentType = runtime.ContentTypeJSON
	case "yaml":
		contentType = "application/yaml"
	default:
		return fmt.Errorf("unsupported output format: %s", outputFormat)

	}
	prettyEnc, err := prettyEncoder(codecs, contentType, ssv1alpha1.SchemeGroupVersion)
	if err != nil {
		return err
	}

	buf, err := runtime.Encode(prettyEnc, ssecret)
	if err != nil {
		return err
	}

	out.Write(buf)
	fmt.Fprint(out, "\n")

	return nil
}

func main() {
	flag.Parse()
	goflag.CommandLine.Parse([]string{})

	f, err := openCert()
	if err != nil {
		panic(err.Error())
	}
	defer f.Close()

	pubKey, err := parseKey(f)
	if err != nil {
		panic(err.Error())
	}

	if err := seal(os.Stdin, os.Stdout, api.Codecs, pubKey); err != nil {
		panic(err.Error())
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"os/signal"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"golang.org/x/sync/errgroup"
	"k8s.io/api/core/v1"

	"github.com/coreos/prometheus-operator/pkg/alertmanager"
	"github.com/coreos/prometheus-operator/pkg/api"
	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	prometheuscontroller "github.com/coreos/prometheus-operator/pkg/prometheus"
	"github.com/go-kit/kit/log"
)

var (
	cfg prometheuscontroller.Config
)

func init() {
	cfg.CrdKinds = monitoringv1.DefaultCrdKinds
	flagset := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	flagset.StringVar(&cfg.Host, "apiserver", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
	flagset.StringVar(&cfg.TLSConfig.CertFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.KeyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.CAFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
	flagset.StringVar(&cfg.KubeletObject, "kubelet-service", "", "Service/Endpoints object to write kubelets into in format \"namespace/name\"")
	flagset.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
	flagset.StringVar(&cfg.PrometheusConfigReloader, "prometheus-config-reloader", "quay.io/coreos/prometheus-config-reloader:v0.0.2", "Config and rule reload image")
	flagset.StringVar(&cfg.ConfigReloaderImage, "config-reloader-image", "quay.io/coreos/configmap-reload:v0.0.1", "Reload Image")
	flagset.StringVar(&cfg.AlertmanagerDefaultBaseImage, "alertmanager-default-base-image", "quay.io/prometheus/alertmanager", "Alertmanager default base image")
	flagset.StringVar(&cfg.PrometheusDefaultBaseImage, "prometheus-default-base-image", "quay.io/prometheus/prometheus", "Prometheus default base image")
	flagset.StringVar(&cfg.Namespace, "namespace", v1.NamespaceAll, "Namespace to scope the interaction of the Prometheus Operator and the apiserver.")
	flagset.Var(&cfg.Labels, "labels", "Labels to be add to all resources created by the operator")
	flagset.StringVar(&cfg.CrdGroup, "crd-apigroup", monitoringv1.Group, "prometheus CRD  API group name")
	flagset.Var(&cfg.CrdKinds, "crd-kinds", " - EXPERIMENTAL (could be removed in future releases) - customize CRD kind names")
	flagset.Parse(os.Args[1:])

}

func Main() int {
	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
	logger = log.With(logger, "ts", log.DefaultTimestampUTC)
	logger = log.With(logger, "caller", log.DefaultCaller)

	r := prometheus.NewRegistry()
	r.MustRegister(prometheus.NewGoCollector())

	po, err := prometheuscontroller.New(cfg, log.With(logger, "component", "prometheusoperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating prometheus controller failed: ", err)
		return 1
	}

	ao, err := alertmanager.New(cfg, log.With(logger, "component", "alertmanageroperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating alertmanager controller failed: ", err)
		return 1
	}

	mux := http.NewServeMux()
	web, err := api.New(cfg, log.With(logger, "component", "api"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating api failed: ", err)
		return 1
	}

	web.Register(mux)
	l, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Fprint(os.Stderr, "listening port 8080 failed", err)
		return 1
	}

	po.RegisterMetrics(r)
	ao.RegisterMetrics(r)
	mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
	mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))

	ctx, cancel := context.WithCancel(context.Background())
	wg, ctx := errgroup.WithContext(ctx)

	wg.Go(func() error { return po.Run(ctx.Done()) })
	wg.Go(func() error { return ao.Run(ctx.Done()) })

	srv := &http.Server{Handler: mux}
	go srv.Serve(l)

	term := make(chan os.Signal)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)

	select {
	case <-term:
		logger.Log("msg", "Received SIGTERM, exiting gracefully...")
	case <-ctx.Done():
	}

	cancel()
	if err := wg.Wait(); err != nil {
		logger.Log("msg", "Unhandled error received. Exiting...", "err", err)
		return 1
	}

	return 0
}

func main() {
	os.Exit(Main())
}
<commit_msg>for glog, re-using flag.Commandline instead of a new flagset<commit_after>// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"os/signal"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"golang.org/x/sync/errgroup"
	"k8s.io/api/core/v1"

	"github.com/coreos/prometheus-operator/pkg/alertmanager"
	"github.com/coreos/prometheus-operator/pkg/api"
	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	prometheuscontroller "github.com/coreos/prometheus-operator/pkg/prometheus"
	"github.com/go-kit/kit/log"
)

var (
	cfg prometheuscontroller.Config
)

func init() {
	cfg.CrdKinds = monitoringv1.DefaultCrdKinds
	flagset := flag.CommandLine
	flagset.StringVar(&cfg.Host, "apiserver", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
	flagset.StringVar(&cfg.TLSConfig.CertFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.KeyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.CAFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
	flagset.StringVar(&cfg.KubeletObject, "kubelet-service", "", "Service/Endpoints object to write kubelets into in format \"namespace/name\"")
	flagset.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
	flagset.StringVar(&cfg.PrometheusConfigReloader, "prometheus-config-reloader", "quay.io/coreos/prometheus-config-reloader:v0.0.2", "Config and rule reload image")
	flagset.StringVar(&cfg.ConfigReloaderImage, "config-reloader-image", "quay.io/coreos/configmap-reload:v0.0.1", "Reload Image")
	flagset.StringVar(&cfg.AlertmanagerDefaultBaseImage, "alertmanager-default-base-image", "quay.io/prometheus/alertmanager", "Alertmanager default base image")
	flagset.StringVar(&cfg.PrometheusDefaultBaseImage, "prometheus-default-base-image", "quay.io/prometheus/prometheus", "Prometheus default base image")
	flagset.StringVar(&cfg.Namespace, "namespace", v1.NamespaceAll, "Namespace to scope the interaction of the Prometheus Operator and the apiserver.")
	flagset.Var(&cfg.Labels, "labels", "Labels to be add to all resources created by the operator")
	flagset.StringVar(&cfg.CrdGroup, "crd-apigroup", monitoringv1.Group, "prometheus CRD  API group name")
	flagset.Var(&cfg.CrdKinds, "crd-kinds", " - EXPERIMENTAL (could be removed in future releases) - customize CRD kind names")
	flagset.Parse(os.Args[1:])

}

func Main() int {
	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
	logger = log.With(logger, "ts", log.DefaultTimestampUTC)
	logger = log.With(logger, "caller", log.DefaultCaller)

	r := prometheus.NewRegistry()
	r.MustRegister(prometheus.NewGoCollector())

	po, err := prometheuscontroller.New(cfg, log.With(logger, "component", "prometheusoperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating prometheus controller failed: ", err)
		return 1
	}

	ao, err := alertmanager.New(cfg, log.With(logger, "component", "alertmanageroperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating alertmanager controller failed: ", err)
		return 1
	}

	mux := http.NewServeMux()
	web, err := api.New(cfg, log.With(logger, "component", "api"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating api failed: ", err)
		return 1
	}

	web.Register(mux)
	l, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Fprint(os.Stderr, "listening port 8080 failed", err)
		return 1
	}

	po.RegisterMetrics(r)
	ao.RegisterMetrics(r)
	mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
	mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))

	ctx, cancel := context.WithCancel(context.Background())
	wg, ctx := errgroup.WithContext(ctx)

	wg.Go(func() error { return po.Run(ctx.Done()) })
	wg.Go(func() error { return ao.Run(ctx.Done()) })

	srv := &http.Server{Handler: mux}
	go srv.Serve(l)

	term := make(chan os.Signal)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)

	select {
	case <-term:
		logger.Log("msg", "Received SIGTERM, exiting gracefully...")
	case <-ctx.Done():
	}

	cancel()
	if err := wg.Wait(); err != nil {
		logger.Log("msg", "Unhandled error received. Exiting...", "err", err)
		return 1
	}

	return 0
}

func main() {
	os.Exit(Main())
}
<|endoftext|>


<commit_before>// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/doc"
	"go/parser"
	"go/token"
	"reflect"
	"strings"
)

const (
	firstParagraph = `

     Note: Starting with v0.12.0, Prometheus Operator requires use of Kubernetes v1.7.x and up.

# API Docs

This Document documents the types introduced by the Prometheus Operator to be consumed by users.

> Note this document is generated from code comments. When contributing a change to this document please do so by changing the code comments.`
)

var (
	links = map[string]string{
		"metav1.ObjectMeta":        "https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#objectmeta-v1-meta",
		"metav1.ListMeta":          "https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#listmeta-v1-meta",
		"metav1.LabelSelector":     "https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#labelselector-v1-meta",
		"v1.ResourceRequirements":  "https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#resourcerequirements-v1-core",
		"v1.LocalObjectReference":  "https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#localobjectreference-v1-core",
		"v1.SecretKeySelector":     "https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#secretkeyselector-v1-core",
		"v1.PersistentVolumeClaim": "https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#persistentvolumeclaim-v1-core",
		"v1.EmptyDirVolumeSource":  "https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#emptydirvolumesource-v1-core",
	}

	selfLinks = map[string]string{}
)

func toSectionLink(name string) string {
	name = strings.ToLower(name)
	name = strings.Replace(name, " ", "-", -1)
	return name
}

func printTOC(types []KubeTypes) {
	fmt.Printf("\n## Table of Contents\n")
	for _, t := range types {
		strukt := t[0]
		fmt.Printf("* [%s](#%s)\n", strukt.Name, toSectionLink(strukt.Name))
	}
}

func printAPIDocs(path string) {
	fmt.Println(firstParagraph)

	types := ParseDocumentationFrom(path)
	for _, t := range types {
		strukt := t[0]
		selfLinks[strukt.Name] = "#" + strings.ToLower(strukt.Name)
	}

	// we need to parse once more to now add the self links
	types = ParseDocumentationFrom(path)

	printTOC(types)

	for _, t := range types {
		strukt := t[0]
		fmt.Printf("\n## %s\n\n%s\n\n", strukt.Name, strukt.Doc)

		fmt.Println("| Field | Description | Scheme | Required |")
		fmt.Println("| ----- | ----------- | ------ | -------- |")
		fields := t[1:(len(t))]
		for _, f := range fields {
			fmt.Println("|", f.Name, "|", f.Doc, "|", f.Type, "|", f.Mandatory, "|")
		}
		fmt.Println("")
		fmt.Println("[Back to TOC](#table-of-contents)")
	}
}

// Pair of strings. We keed the name of fields and the doc
type Pair struct {
	Name, Doc, Type string
	Mandatory       bool
}

// KubeTypes is an array to represent all available types in a parsed file. [0] is for the type itself
type KubeTypes []Pair

// ParseDocumentationFrom gets all types' documentation and returns them as an
// array. Each type is again represented as an array (we have to use arrays as we
// need to be sure for the order of the fields). This function returns fields and
// struct definitions that have no documentation as {name, ""}.
func ParseDocumentationFrom(src string) []KubeTypes {
	var docForTypes []KubeTypes

	pkg := astFrom(src)

	for _, kubType := range pkg.Types {
		if structType, ok := kubType.Decl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType); ok {
			var ks KubeTypes
			ks = append(ks, Pair{kubType.Name, fmtRawDoc(kubType.Doc), "", false})

			for _, field := range structType.Fields.List {
				typeString := fieldType(field.Type)
				fieldMandatory := fieldRequired(field)
				if n := fieldName(field); n != "-" {
					fieldDoc := fmtRawDoc(field.Doc.Text())
					ks = append(ks, Pair{n, fieldDoc, typeString, fieldMandatory})
				}
			}
			docForTypes = append(docForTypes, ks)
		}
	}

	return docForTypes
}

func astFrom(filePath string) *doc.Package {
	fset := token.NewFileSet()
	m := make(map[string]*ast.File)

	f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	m[filePath] = f
	apkg, _ := ast.NewPackage(fset, m, nil, nil)

	return doc.New(apkg, "", 0)
}

func fmtRawDoc(rawDoc string) string {
	var buffer bytes.Buffer
	delPrevChar := func() {
		if buffer.Len() > 0 {
			buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
		}
	}

	// Ignore all lines after ---
	rawDoc = strings.Split(rawDoc, "---")[0]

	for _, line := range strings.Split(rawDoc, "\n") {
		line = strings.TrimRight(line, " ")
		leading := strings.TrimLeft(line, " ")
		switch {
		case len(line) == 0: // Keep paragraphs
			delPrevChar()
			buffer.WriteString("\n\n")
		case strings.HasPrefix(leading, "TODO"): // Ignore one line TODOs
		case strings.HasPrefix(leading, "+"): // Ignore instructions to go2idl
		default:
			if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
				delPrevChar()
				line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
			} else {
				line += " "
			}
			buffer.WriteString(line)
		}
	}

	postDoc := strings.TrimRight(buffer.String(), "\n")
	postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
	postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
	postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
	postDoc = strings.Replace(postDoc, "\t", "\\t", -1)

	return postDoc
}

func toLink(typeName string) string {
	selfLink, hasSelfLink := selfLinks[typeName]
	if hasSelfLink {
		return wrapInLink(typeName, selfLink)
	}

	link, hasLink := links[typeName]
	if hasLink {
		return wrapInLink(typeName, link)
	}

	return typeName
}

func wrapInLink(text, link string) string {
	return fmt.Sprintf("[%s](%s)", text, link)
}

// fieldName returns the name of the field as it should appear in JSON format
// "-" indicates that this field is not part of the JSON representation
func fieldName(field *ast.Field) string {
	jsonTag := ""
	if field.Tag != nil {
		jsonTag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
		if strings.Contains(jsonTag, "inline") {
			return "-"
		}
	}

	jsonTag = strings.Split(jsonTag, ",")[0] // This can return "-"
	if jsonTag == "" {
		if field.Names != nil {
			return field.Names[0].Name
		}
		return field.Type.(*ast.Ident).Name
	}
	return jsonTag
}

// fieldRequired returns whether a field is a required field.
func fieldRequired(field *ast.Field) bool {
	jsonTag := ""
	if field.Tag != nil {
		jsonTag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
		return !strings.Contains(jsonTag, "omitempty")
	}

	return false
}

func fieldType(typ ast.Expr) string {
	switch typ.(type) {
	case *ast.Ident:
		return toLink(typ.(*ast.Ident).Name)
	case *ast.StarExpr:
		return "*" + toLink(fieldType(typ.(*ast.StarExpr).X))
	case *ast.SelectorExpr:
		e := typ.(*ast.SelectorExpr)
		pkg := e.X.(*ast.Ident)
		t := e.Sel
		return toLink(pkg.Name + "." + t.Name)
	case *ast.ArrayType:
		return "[]" + toLink(fieldType(typ.(*ast.ArrayType).Elt))
	case *ast.MapType:
		mapType := typ.(*ast.MapType)
		return "map[" + toLink(fieldType(mapType.Key)) + "]" + toLink(fieldType(mapType.Value))
	default:
		return ""
	}
}
<commit_msg>Update dead links to latest version of Kubernetes docs<commit_after>// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/doc"
	"go/parser"
	"go/token"
	"reflect"
	"strings"
)

const (
	firstParagraph = `

     Note: Starting with v0.12.0, Prometheus Operator requires use of Kubernetes v1.7.x and up.

# API Docs

This Document documents the types introduced by the Prometheus Operator to be consumed by users.

> Note this document is generated from code comments. When contributing a change to this document please do so by changing the code comments.`
)

var (
	links = map[string]string{
		"metav1.ObjectMeta":        "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#objectmeta-v1-meta",
		"metav1.ListMeta":          "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#listmeta-v1-meta",
		"metav1.LabelSelector":     "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#labelselector-v1-meta",
		"v1.ResourceRequirements":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#resourcerequirements-v1-core",
		"v1.LocalObjectReference":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#localobjectreference-v1-core",
		"v1.SecretKeySelector":     "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#secretkeyselector-v1-core",
		"v1.PersistentVolumeClaim": "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#persistentvolumeclaim-v1-core",
		"v1.EmptyDirVolumeSource":  "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#emptydirvolumesource-v1-core",
	}

	selfLinks = map[string]string{}
)

func toSectionLink(name string) string {
	name = strings.ToLower(name)
	name = strings.Replace(name, " ", "-", -1)
	return name
}

func printTOC(types []KubeTypes) {
	fmt.Printf("\n## Table of Contents\n")
	for _, t := range types {
		strukt := t[0]
		fmt.Printf("* [%s](#%s)\n", strukt.Name, toSectionLink(strukt.Name))
	}
}

func printAPIDocs(path string) {
	fmt.Println(firstParagraph)

	types := ParseDocumentationFrom(path)
	for _, t := range types {
		strukt := t[0]
		selfLinks[strukt.Name] = "#" + strings.ToLower(strukt.Name)
	}

	// we need to parse once more to now add the self links
	types = ParseDocumentationFrom(path)

	printTOC(types)

	for _, t := range types {
		strukt := t[0]
		fmt.Printf("\n## %s\n\n%s\n\n", strukt.Name, strukt.Doc)

		fmt.Println("| Field | Description | Scheme | Required |")
		fmt.Println("| ----- | ----------- | ------ | -------- |")
		fields := t[1:(len(t))]
		for _, f := range fields {
			fmt.Println("|", f.Name, "|", f.Doc, "|", f.Type, "|", f.Mandatory, "|")
		}
		fmt.Println("")
		fmt.Println("[Back to TOC](#table-of-contents)")
	}
}

// Pair of strings. We keed the name of fields and the doc
type Pair struct {
	Name, Doc, Type string
	Mandatory       bool
}

// KubeTypes is an array to represent all available types in a parsed file. [0] is for the type itself
type KubeTypes []Pair

// ParseDocumentationFrom gets all types' documentation and returns them as an
// array. Each type is again represented as an array (we have to use arrays as we
// need to be sure for the order of the fields). This function returns fields and
// struct definitions that have no documentation as {name, ""}.
func ParseDocumentationFrom(src string) []KubeTypes {
	var docForTypes []KubeTypes

	pkg := astFrom(src)

	for _, kubType := range pkg.Types {
		if structType, ok := kubType.Decl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType); ok {
			var ks KubeTypes
			ks = append(ks, Pair{kubType.Name, fmtRawDoc(kubType.Doc), "", false})

			for _, field := range structType.Fields.List {
				typeString := fieldType(field.Type)
				fieldMandatory := fieldRequired(field)
				if n := fieldName(field); n != "-" {
					fieldDoc := fmtRawDoc(field.Doc.Text())
					ks = append(ks, Pair{n, fieldDoc, typeString, fieldMandatory})
				}
			}
			docForTypes = append(docForTypes, ks)
		}
	}

	return docForTypes
}

func astFrom(filePath string) *doc.Package {
	fset := token.NewFileSet()
	m := make(map[string]*ast.File)

	f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	m[filePath] = f
	apkg, _ := ast.NewPackage(fset, m, nil, nil)

	return doc.New(apkg, "", 0)
}

func fmtRawDoc(rawDoc string) string {
	var buffer bytes.Buffer
	delPrevChar := func() {
		if buffer.Len() > 0 {
			buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
		}
	}

	// Ignore all lines after ---
	rawDoc = strings.Split(rawDoc, "---")[0]

	for _, line := range strings.Split(rawDoc, "\n") {
		line = strings.TrimRight(line, " ")
		leading := strings.TrimLeft(line, " ")
		switch {
		case len(line) == 0: // Keep paragraphs
			delPrevChar()
			buffer.WriteString("\n\n")
		case strings.HasPrefix(leading, "TODO"): // Ignore one line TODOs
		case strings.HasPrefix(leading, "+"): // Ignore instructions to go2idl
		default:
			if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
				delPrevChar()
				line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
			} else {
				line += " "
			}
			buffer.WriteString(line)
		}
	}

	postDoc := strings.TrimRight(buffer.String(), "\n")
	postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
	postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
	postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
	postDoc = strings.Replace(postDoc, "\t", "\\t", -1)

	return postDoc
}

func toLink(typeName string) string {
	selfLink, hasSelfLink := selfLinks[typeName]
	if hasSelfLink {
		return wrapInLink(typeName, selfLink)
	}

	link, hasLink := links[typeName]
	if hasLink {
		return wrapInLink(typeName, link)
	}

	return typeName
}

func wrapInLink(text, link string) string {
	return fmt.Sprintf("[%s](%s)", text, link)
}

// fieldName returns the name of the field as it should appear in JSON format
// "-" indicates that this field is not part of the JSON representation
func fieldName(field *ast.Field) string {
	jsonTag := ""
	if field.Tag != nil {
		jsonTag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
		if strings.Contains(jsonTag, "inline") {
			return "-"
		}
	}

	jsonTag = strings.Split(jsonTag, ",")[0] // This can return "-"
	if jsonTag == "" {
		if field.Names != nil {
			return field.Names[0].Name
		}
		return field.Type.(*ast.Ident).Name
	}
	return jsonTag
}

// fieldRequired returns whether a field is a required field.
func fieldRequired(field *ast.Field) bool {
	jsonTag := ""
	if field.Tag != nil {
		jsonTag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
		return !strings.Contains(jsonTag, "omitempty")
	}

	return false
}

func fieldType(typ ast.Expr) string {
	switch typ.(type) {
	case *ast.Ident:
		return toLink(typ.(*ast.Ident).Name)
	case *ast.StarExpr:
		return "*" + toLink(fieldType(typ.(*ast.StarExpr).X))
	case *ast.SelectorExpr:
		e := typ.(*ast.SelectorExpr)
		pkg := e.X.(*ast.Ident)
		t := e.Sel
		return toLink(pkg.Name + "." + t.Name)
	case *ast.ArrayType:
		return "[]" + toLink(fieldType(typ.(*ast.ArrayType).Elt))
	case *ast.MapType:
		mapType := typ.(*ast.MapType)
		return "map[" + toLink(fieldType(mapType.Key)) + "]" + toLink(fieldType(mapType.Value))
	default:
		return ""
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

var rootCommand = &cobra.Command{
	Use:        "etcd-tester-reporter",
	Short:      "etcd-tester-reporter is etcd functional-tester reporter.",
	SuggestFor: []string{"etcd-tester-reportr", "etcdtesterreporter"},
}

func init() {
	cobra.EnablePrefixMatching = true
}

var metricsName string
var metricsEndpoint string

var dbHost string
var dbPort int
var dbUser string
var dbPassword string

func init() {
	rootCommand.PersistentFlags().StringVar(&metricsName, "metrics-name", "", "metrics name")
	rootCommand.PersistentFlags().StringVar(&metricsEndpoint, "metrics-endpoint", "", "metrics endpoint")

	rootCommand.PersistentFlags().StringVar(&dbHost, "db-host", "", "database host")
	rootCommand.PersistentFlags().IntVar(&dbPort, "db-port", 3306, "database port")
	rootCommand.PersistentFlags().StringVar(&dbUser, "db-user", "root", "database user")
	rootCommand.PersistentFlags().StringVar(&dbPassword, "db-password", "", "database password")

	rootCommand.AddCommand(pingCommand)
}

func main() {
	if err := rootCommand.Execute(); err != nil {
		fmt.Fprintln(os.Stdout, err)
		os.Exit(1)
	}
}
<commit_msg>cmd/reporter: change cmd<commit_after>// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

var rootCommand = &cobra.Command{
	Use:        "reporter",
	Short:      "reporter is etcd functional-tester reporter.",
	SuggestFor: []string{"etcd-tester-reportr", "etcdtesterreporter"},
}

func init() {
	cobra.EnablePrefixMatching = true
}

var metricsName string
var metricsEndpoint string

var dbHost string
var dbPort int
var dbUser string
var dbPassword string

func init() {
	rootCommand.PersistentFlags().StringVar(&metricsName, "metrics-name", "", "metrics name")
	rootCommand.PersistentFlags().StringVar(&metricsEndpoint, "metrics-endpoint", "", "metrics endpoint")

	rootCommand.PersistentFlags().StringVar(&dbHost, "db-host", "", "database host")
	rootCommand.PersistentFlags().IntVar(&dbPort, "db-port", 3306, "database port")
	rootCommand.PersistentFlags().StringVar(&dbUser, "db-user", "root", "database user")
	rootCommand.PersistentFlags().StringVar(&dbPassword, "db-password", "", "database password")

	rootCommand.AddCommand(pingCommand)
}

func main() {
	if err := rootCommand.Execute(); err != nil {
		fmt.Fprintln(os.Stdout, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"

	"gopkg.in/adsi.v0"
	"gopkg.in/dfsr.v0/config"
)

func main() {
	flag.Parse()

	client, err := adsi.NewClient()
	if err != nil {
		log.Fatal(err)
	}

	var domain = flag.Arg(0)

	if domain == "" {
		dnc, dncErr := rootDNC(client)
		if dncErr != nil {
			log.Fatal(dncErr)
		}
		domain = dnc
	}

	d, err := config.Domain(client, domain)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("      Domain: %-51s ID: %v DN: %-30s Duration: %v\n", d.Description, d.ID, d.DN, d.ConfigDuration)
	for i := 0; i < len(d.Groups); i++ {
		group := &d.Groups[i]
		fmt.Printf("[%3d]   Group: %-50s ID: %v Duration: %v\n", i, group.Name, group.ID, group.ConfigDuration)
		for f := 0; f < len(group.Folders); f++ {
			folder := &group.Folders[f]
			fmt.Printf("          Folder: %-47s ID: %v\n", folder.Name, folder.ID)
		}
		for m := 0; m < len(group.Members); m++ {
			member := &group.Members[m]
			fmt.Printf("          Member: %-47s ID: %v Computer: %s\n", member.Name, member.ID, member.Computer.Host)
			for c := 0; c < len(member.Connections); c++ {
				conn := &member.Connections[c]
				fmt.Printf("            Connection: %-41s ID: %v Computer: %s\n", conn.Name, conn.ID, conn.Computer.Host)
			}
		}
	}
	fmt.Printf("Duration: %v\n", d.ConfigDuration)
}

func rootDNC(client *adsi.Client) (string, error) {
	rootDSE, err := client.Open("LDAP://RootDSE")
	if err != nil {
		return "", err
	}
	defer rootDSE.Close()

	return rootDSE.AttrString("rootDomainNamingContext")
}
<commit_msg>rglist now shows enabled/disabled DFSR connection status<commit_after>package main

import (
	"flag"
	"fmt"
	"log"

	"gopkg.in/adsi.v0"
	"gopkg.in/dfsr.v0/config"
)

func main() {
	flag.Parse()

	client, err := adsi.NewClient()
	if err != nil {
		log.Fatal(err)
	}

	var domain = flag.Arg(0)

	if domain == "" {
		dnc, dncErr := rootDNC(client)
		if dncErr != nil {
			log.Fatal(dncErr)
		}
		domain = dnc
	}

	d, err := config.Domain(client, domain)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("      Domain: %-51s ID: %v DN: %-30s Duration: %v\n", d.Description, d.ID, d.DN, d.ConfigDuration)
	for i := 0; i < len(d.Groups); i++ {
		group := &d.Groups[i]
		fmt.Printf("[%3d]   Group: %-50s ID: %v Duration: %v\n", i, group.Name, group.ID, group.ConfigDuration)
		for f := 0; f < len(group.Folders); f++ {
			folder := &group.Folders[f]
			fmt.Printf("          Folder: %-47s ID: %v\n", folder.Name, folder.ID)
		}
		for m := 0; m < len(group.Members); m++ {
			member := &group.Members[m]
			fmt.Printf("          Member: %-47s ID: %v Computer: %s\n", member.Name, member.ID, member.Computer.Host)
			for c := 0; c < len(member.Connections); c++ {
				conn := &member.Connections[c]
				enabledMark := " "
				if conn.Enabled {
					enabledMark = "x"
				}
				fmt.Printf("            Connection[%s]: %-39s ID: %v Computer: %s\n", enabledMark, conn.Name, conn.ID, conn.Computer.Host)
			}
		}
	}
	fmt.Printf("Duration: %v\n", d.ConfigDuration)
}

func rootDNC(client *adsi.Client) (string, error) {
	rootDSE, err := client.Open("LDAP://RootDSE")
	if err != nil {
		return "", err
	}
	defer rootDSE.Close()

	return rootDSE.AttrString("rootDomainNamingContext")
}
<|endoftext|>


<commit_before>package signals

import "encoding/gob"
import "sync"

func init() {
	gob.Register(Shifted{})
	gob.Register(Offset{})
	gob.Register(Compressed{})
	gob.Register(Looped{})
	gob.Register(Inverted{})
	gob.Register(Reversed{})
	gob.Register(Reflected{})
	gob.Register(RateModulated{})
	gob.Register(Triggered{})
	gob.Register(Segmented{})
}

// a Signal whose values are moved, in x, by 'Shift'.
type Shifted struct {
	Signal
	Shift x
}

func (s Shifted) property(p x) y {
	return s.Signal.property(p - s.Shift)
}

// a LimitedSignal whose values are moved, in x, by 'Offset'.
type Offset struct {
	LimitedSignal
	Offset x
}

func (s Offset) property(p x) y {
	return s.LimitedSignal.property(p - s.Offset)
}
func (s Offset) MaxX() x {
	return s.LimitedSignal.MaxX()+s.Offset
}

// a Signal that scales the x of another Signal
type Compressed struct {
	 Signal
	Factor float32
}

func (s Compressed) property(p x) y {
	return s.Signal.property(x(float32(p) * s.Factor))
}

func (s Compressed) MaxX() x {
	return x(float32(s.Signal.(LimitedSignal).MaxX())/s.Factor)
}

func (s Compressed) Period() x {
	return x(float32(s.Signal.(PeriodicSignal).Period())/s.Factor)
}

// a PeriodicSignal that is a Signal repeated with Loop length x.
type Looped struct {
	Signal
	Loop x
}

func (s Looped) property(p x) y {
	return s.Signal.property(p % s.Loop)
}

func (s Looped) Period() x {
	return s.Loop
}

// a PeriodicSignal that is repeating loop of Cycles number of repeats of another PeriodicSignal.
// if the PeriodicSignal is actually precisely repeating, then an integer value of Cycles, results in no change.
type Repeated struct {
	PeriodicSignal
	Cycles float32
}

func (s Repeated) Period() x {
	return x(float32(s.PeriodicSignal.Period()) * s.Cycles)
}

func (s Repeated) property(p x) y {
	return s.PeriodicSignal.property((p % s.Period()) % s.PeriodicSignal.Period())
}

// a Signal that produces y values that are the negative of another Signals y values
type Inverted struct {
	Signal
}

func (s Inverted) property(p x) y {
	return -s.Signal.property(p)
}

// a Signal that returns y's that are for the -ve x of another Signal
type Reversed struct {
	Signal
}

func (s Reversed) property(p x) y {
	return s.Signal.property(-p)
}

// a Signal that produces values that are flipped over, (Maxy<->zero) of another Signal
type Reflected struct {
	Signal
}

func (s Reflected) property(p x) y {
	if r := s.Signal.property(p); r < 0 {
		return -unitY - r
	} else {
		return unitY - r
	}
}

// a Signal that stretches the x values of another Signal, in proportion to the value of a modulation Signal
type RateModulated struct {
	Signal
	Modulation Signal
	Factor     x
}

func (s RateModulated) property(p x) y {
	return s.Signal.property(p + MultiplyX(float64(s.Modulation.property(p))/unitYfloat64, s.Factor))
}

func (s RateModulated) Period() x {
	if  mps,ok:= s.Modulation.(PeriodicSignal);ok {
		if  ps,ok:= s.Signal.(PeriodicSignal);ok {
			if mpsp,psp:=mps.Period(),ps.Period(); mpsp>psp {
				return mpsp
			}else{
				return psp
			}
		}else{
			return mps.Period()
		}
	}else{
		s.Signal.(PeriodicSignal).Period()
	}
	return 0
}

// Segmented is a Signal that is a sequence of equal width, uniform gradient, segments, that approximate another Signal.
// repeated calls within the same segment, are generated from cached end values, so avoiding calls to the embedded Signal.
type Segmented struct {
	Signal
	Width x
	ends *endInfo
}

type endInfo struct {
	x1, x2, l1, l2 x
}

var segmentedMutex = &sync.Mutex{}

func NewSegmented(s Signal, width x) Segmented {
	return Segmented{s, width, &endInfo{}}
}

func (s Segmented) property(p x) (value y) {
	temp := p % s.Width
	segmentedMutex.Lock()
	if p-temp != s.ends.x1 || p+s.Width-temp != s.ends.x2 {
		s.ends.x1 = p - temp
		s.ends.x2 = p + s.Width - temp
		s.ends.l1 = x(s.Signal.property(s.ends.x1))
		s.ends.l2 = x(s.Signal.property(s.ends.x2))/s.Width - s.ends.l1/ s.Width
	}
	value=y(s.ends.l1 + s.ends.l2*temp)
	segmentedMutex.Unlock()
	return
}

func (s Segmented) Period() x {
	// TODO could use shortest of width and signal periods?
	return s.Width
}

// Triggered shifts a Signal's x so the Signal crosses a trigger y at zero x.
// it searches with a Resolution, from Shift+Resolution to MaxShift, then from 0 to Shift.
// Shift can be set initially, then is set to the last found trigger, so subsequent uses find new crossings, and wraps round.
// Rising can be alternated to find either way crossing
type Triggered struct {
	Signal
	Trigger    y
	Rising     bool
	Resolution x
	MaxShift   x
	Found      *searchInfo // by being a pointer this is mutable in methods, without needing a pointer receiver.
}

type searchInfo struct {
	Shift   x
	trigger y
	rising  bool
}

func NewTriggered(s Signal, trigger y, rising bool, res, max x) Triggered {
	return Triggered{s, trigger, rising, res, max, &searchInfo{}}
}

func (s Triggered) property(p x) y {
	if s.Trigger != s.Found.trigger || s.Found.rising != s.Rising {
		s.Found.trigger = s.Trigger
		s.Found.rising = s.Rising
		if s.Rising && s.Signal.property(s.Found.Shift) > s.Trigger || !s.Rising && s.Signal.property(s.Found.Shift) < s.Trigger {
			s.Found.Shift += s.Resolution
		}
		for t := s.Found.Shift; t <= s.MaxShift; t += s.Resolution {
			if s.Rising && s.Signal.property(t) > s.Trigger || !s.Rising && s.Signal.property(t) < s.Trigger {
				s.Found.Shift = t
				return s.Signal.property(t)
			}
		}
		for t := x(0); t < s.Found.Shift; t += s.Resolution {
			if s.Rising && s.Signal.property(t) > s.Trigger || !s.Rising && s.Signal.property(t) < s.Trigger {
				s.Found.Shift = t
				return s.Signal.property(t)
			}
		}
		s.Found.Shift = 0
	}
	return s.Signal.property(p + s.Found.Shift)
}

<commit_msg>comment<commit_after>package signals

import "encoding/gob"
import "sync"

func init() {
	gob.Register(Shifted{})
	gob.Register(Offset{})
	gob.Register(Compressed{})
	gob.Register(Looped{})
	gob.Register(Inverted{})
	gob.Register(Reversed{})
	gob.Register(Reflected{})
	gob.Register(RateModulated{})
	gob.Register(Triggered{})
	gob.Register(Segmented{})
}

// a Signal whose values are moved, in x, by 'Shift'.
type Shifted struct {
	Signal
	Shift x
}

func (s Shifted) property(p x) y {
	return s.Signal.property(p - s.Shift)
}

// a LimitedSignal whose values are moved, in x, by 'Offset'.
type Offset struct {
	LimitedSignal
	Offset x
}

func (s Offset) property(p x) y {
	return s.LimitedSignal.property(p - s.Offset)
}
func (s Offset) MaxX() x {
	return s.LimitedSignal.MaxX()+s.Offset
}

// a Signal that scales the x of another Signal
type Compressed struct {
	 Signal
	Factor float32
}

func (s Compressed) property(p x) y {
	return s.Signal.property(x(float32(p) * s.Factor))
}

func (s Compressed) MaxX() x {
	return x(float32(s.Signal.(LimitedSignal).MaxX())/s.Factor)
}

func (s Compressed) Period() x {
	return x(float32(s.Signal.(PeriodicSignal).Period())/s.Factor)
}

// a PeriodicSignal that is a Signal repeated with Loop length x.
type Looped struct {
	Signal
	Loop x
}

func (s Looped) property(p x) y {
	return s.Signal.property(p % s.Loop)
}

func (s Looped) Period() x {
	return s.Loop
}

// a PeriodicSignal that is repeating loop of Cycles number of repeats of another PeriodicSignal.
// if the PeriodicSignal is actually precisely repeating, then an integer value of Cycles, results in no change.
type Repeated struct {
	PeriodicSignal
	Cycles float32
}

func (s Repeated) Period() x {
	return x(float32(s.PeriodicSignal.Period()) * s.Cycles)
}

func (s Repeated) property(p x) y {
	return s.PeriodicSignal.property((p % s.Period()) % s.PeriodicSignal.Period())
}

// a Signal that produces y values that are the negative of another Signals y values
type Inverted struct {
	Signal
}

func (s Inverted) property(p x) y {
	return -s.Signal.property(p)
}

// a Signal that returns y's that are for the -ve x of another Signal
type Reversed struct {
	Signal
}

func (s Reversed) property(p x) y {
	return s.Signal.property(-p)
}

// a Signal that produces values that are flipped over, (Maxy<->zero) of another Signal
type Reflected struct {
	Signal
}

func (s Reflected) property(p x) y {
	if r := s.Signal.property(p); r < 0 {
		return -unitY - r
	} else {
		return unitY - r
	}
}

// a Signal that stretches the x values of another Signal, in proportion to the value of a modulation Signal
type RateModulated struct {
	Signal
	Modulation Signal
	Factor     x
}

func (s RateModulated) property(p x) y {
	return s.Signal.property(p + MultiplyX(float64(s.Modulation.property(p))/unitYfloat64, s.Factor))
}

func (s RateModulated) Period() x {
	if  mps,ok:= s.Modulation.(PeriodicSignal);ok {
		if  ps,ok:= s.Signal.(PeriodicSignal);ok {
			if mpsp,psp:=mps.Period(),ps.Period(); mpsp>psp {
				return mpsp
			}else{
				return psp
			}
		}else{
			return mps.Period()
		}
	}else{
		s.Signal.(PeriodicSignal).Period()
	}
	return 0
}

// Segmented is a Signal that is a sequence of equal width segments each with a uniform gradient, that approximate another Signal.
// Repeated calls within the same segment, are generated from interpolating cached segmant end values, so avoiding calls to the embedded Signal.
type Segmented struct {
	Signal
	Width x
	ends *endInfo
}

type endInfo struct {
	x1, x2, l1, l2 x
}

var segmentedMutex = &sync.Mutex{}

func NewSegmented(s Signal, width x) Segmented {
	return Segmented{s, width, &endInfo{}}
}

func (s Segmented) property(p x) (value y) {
	temp := p % s.Width
	segmentedMutex.Lock()
	if p-temp != s.ends.x1 || p+s.Width-temp != s.ends.x2 {
		s.ends.x1 = p - temp
		s.ends.x2 = p + s.Width - temp
		s.ends.l1 = x(s.Signal.property(s.ends.x1))
		s.ends.l2 = x(s.Signal.property(s.ends.x2))/s.Width - s.ends.l1/ s.Width
	}
	value=y(s.ends.l1 + s.ends.l2*temp)
	segmentedMutex.Unlock()
	return
}

func (s Segmented) Period() x {
	// TODO could use shortest of width and signal periods?
	return s.Width
}

// Triggered shifts a Signal's x so the Signal crosses a trigger y at zero x.
// it searches with a Resolution, from Shift+Resolution to MaxShift, then from 0 to Shift.
// Shift can be set initially, then is set to the last found trigger, so subsequent uses find new crossings, and wraps round.
// Rising can be alternated to find either way crossing
type Triggered struct {
	Signal
	Trigger    y
	Rising     bool
	Resolution x
	MaxShift   x
	Found      *searchInfo // by being a pointer this is mutable in methods, without needing a pointer receiver.
}

type searchInfo struct {
	Shift   x
	trigger y
	rising  bool
}

func NewTriggered(s Signal, trigger y, rising bool, res, max x) Triggered {
	return Triggered{s, trigger, rising, res, max, &searchInfo{}}
}

func (s Triggered) property(p x) y {
	if s.Trigger != s.Found.trigger || s.Found.rising != s.Rising {
		s.Found.trigger = s.Trigger
		s.Found.rising = s.Rising
		if s.Rising && s.Signal.property(s.Found.Shift) > s.Trigger || !s.Rising && s.Signal.property(s.Found.Shift) < s.Trigger {
			s.Found.Shift += s.Resolution
		}
		for t := s.Found.Shift; t <= s.MaxShift; t += s.Resolution {
			if s.Rising && s.Signal.property(t) > s.Trigger || !s.Rising && s.Signal.property(t) < s.Trigger {
				s.Found.Shift = t
				return s.Signal.property(t)
			}
		}
		for t := x(0); t < s.Found.Shift; t += s.Resolution {
			if s.Rising && s.Signal.property(t) > s.Trigger || !s.Rising && s.Signal.property(t) < s.Trigger {
				s.Found.Shift = t
				return s.Signal.property(t)
			}
		}
		s.Found.Shift = 0
	}
	return s.Signal.property(p + s.Found.Shift)
}

<|endoftext|>


<commit_before>package godate

import "testing"

func TestParse(t *testing.T) {

}

func TestDate_Format(t *testing.T) {

}

func TestDate_String(t *testing.T) {

}
<commit_msg>Add format test<commit_after>package godate

import (
	"testing"
	"time"
)

func TestParse(t *testing.T) {
	d, err := Parse(RFC3339, "2017-10-20")
	if err != nil {
		t.Errorf("expected error not raised, but got=%v", err)
	}
	if d.String() != "2017-10-20" {
		t.Errorf("expected String() returns '2017-10-20', but got %s", d.String())
	}

	d, err = Parse("20060102Z", "20171020JST")
	if err != nil {
		t.Errorf("expected error not raised, but got=%v", err)
	}
	if d.String() != "2017-10-20" {
		t.Errorf("expected String() returns '2017-10-20', but got %s", d.String())
	}
	if zn, _ := d.Zone(); zn != "JST" {
		t.Errorf("expected zone name 'JST', but got %s", zn)
	}

	_, err = Parse("20161020", "2017-10-20")
	if err == nil {
		t.Errorf("expected error raised, bug got %v", err)
	}
}

func TestDate_Format(t *testing.T) {
	d := New(2017, 10, 27, time.UTC)
	if d.Format(RFC3339) != "2017-10-27" {
		t.Errorf("expected formatted godate.RFC3339, but got %s", d.Format(RFC3339))
	}
	if d.Format("2006/01/02") != "2017/10/27" {
		t.Errorf("expected formatted to '2006/01/02', but got %s", d.Format("2006/01/02"))
	}
}

func TestDate_String(t *testing.T) {
	d := New(2017, 10, 27, time.UTC)
	if d.String() != "2017-10-27" {
		t.Errorf("expected formatted godate.RFC3339, but got %s", d.String())
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Brian J. Downs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package formatifier

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

// ToPhone will format the provided string as a Phone Number.  Only supports
// US numbers currently.
func ToPhone(theString string, delimiter string) (string, error) {
	if len(theString) < 10 {
		return "", errors.New(lengthError)
	}

	f := New(theString)
	f.removeNonDigits()

	var buffer bytes.Buffer
	count := 0

	if len(f.theString) == 10 {
		buffer.WriteString("(")
		for _, i := range f.theString {
			count++

			buffer.WriteString(string(i))

			if count == 3 {
				buffer.WriteString(") ")
			} else if count == 6 {
				buffer.WriteString(fmt.Sprintf("%s", delimiter))
			}
		}
	} else if len(f.theString) == 11 {
		for _, i := range f.theString {
			count++

			buffer.WriteString(string(i))

			if count == 1 {
				buffer.WriteString(" (")
			} else if count == 4 {
				buffer.WriteString(") ")
			} else if count == 7 {
				buffer.WriteString(fmt.Sprintf("%s", delimiter))
			}
		}
	}
	return buffer.String(), nil
}

// ToURL will format the provided string as a URL.  HTTP and HTTPS
// are the only supported protocols at this time.
func ToURL(theString string, secure bool, subdomain string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.makeLower()

	if secure {
		if len(subdomain) > 0 {
			return fmt.Sprintf("https://%s.%s", subdomain, f.theString), nil
		}
		return fmt.Sprintf("https://%s", f.theString), nil
	}

	if len(subdomain) > 0 {
		return fmt.Sprintf("http://%s.%s", subdomain, f.theString), nil
	}
	return fmt.Sprintf("http://%s", f.theString), nil
}

// ToSSN will format the provided string as a SSN.
func ToSSN(theString string, delimiter string) (string, error) {
	f := New(theString)

	f.removeNonDigits()

	if len(f.theString) != 9 {
		return "", errors.New("ERROR: String needs to be 9 digits for Social Security Numbers")
	}

	var buffer bytes.Buffer
	count := 0

	for _, i := range f.theString {
		count++

		buffer.WriteString(string(i))

		if count == 3 || count == 5 {
			buffer.WriteString(delimiter)
		}
	}
	return buffer.String(), nil
}

// ToLockCombo will format the provided string as a Lock Combo.
func ToLockCombo(theString string, delimiter string) (string, error) {
	f := New(theString)

	f.removeNonDigits()

	if len(f.theString) != 9 {
		return "", errors.New("ERROR: String needs to be 6 digits for Lock Combo format")
	}

	var buffer bytes.Buffer
	count := 0

	for _, i := range f.theString {
		count++

		buffer.WriteString(string(i))

		if count == 2 || count == 4 {
			buffer.WriteString(delimiter)
		}
	}
	return buffer.String(), nil
}

// ToISBN will format the provided string in International Standard Book Number
// (ISBN) format.
func ToISBN(theString string, delimiter string) (string, error) {
	f := New(theString)

	f.removeNonDigits()

	if len(f.theString) != 13 {
		return "", errors.New("ERROR: string must be 13 characters")
	}

	var buffer bytes.Buffer
	count := 0

	for _, i := range f.theString {
		count++

		buffer.WriteString(string(i))

		if count == 3 || count == 4 || count == 6 || count == 12 {
			buffer.WriteString(delimiter)
		}
	}
	return buffer.String(), nil
}

// ToMorseCode will format the provided string in Morse Code.
func ToMorseCode(theString string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.makeLower()

	var buffer bytes.Buffer

	for _, i := range f.theString {
		key := string(i)
		if _, ok := morse[key]; ok {
			buffer.WriteString(morse[key])
		} else if key == " " {
			buffer.WriteString(" ")
		}
	}
	return buffer.String(), nil
}

// ToPirateSpeak will format the provided string in Pirate Speak.
func ToPirateSpeak(theString string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.urlEncodeSpaces()

	response, err := http.Get(fmt.Sprintf(pirateLink, f.theString))
	if err != nil {
		return "", errors.New("ERROR: Unable to convert")
	}
	defer response.Body.Close()

	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", err
	}
	return string(contents), nil
}

// ToIRSA will format the provided string in IRSA.
// International Radio-Telephony Spelling Alphabet
func ToIRSA(theString string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.makeLower()

	var buffer bytes.Buffer

	for _, i := range f.theString {
		key := strings.ToLower(string(i))
		if _, ok := irsa[key]; ok {
			buffer.WriteString(irsa[key] + " ")
		}
	}
	return buffer.String(), nil
}

// ToLeet will format the provided string in Leet Speak.
// TODO(bdowns328) Make select post match random.
func ToLeet(theString string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.makeLower()

	words := strings.Fields(f.theString)
	var buffer bytes.Buffer

	for _, word := range words {
		key := string(word)
		if _, ok := leet[word]; ok {
			buffer.WriteString(leet[key][0] + " ")
		} else {
			for _, i := range word {
				letter := string(i)
				if _, ok := leet[letter]; ok {
					buffer.WriteString(leet[letter][0])
				}
			}
			buffer.WriteString(" ")
		}
	}
	return buffer.String(), nil
}
<commit_msg>added url length check and removed unneeded length check later in the process.<commit_after>// Copyright 2014 Brian J. Downs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package formatifier

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

// ToPhone will format the provided string as a Phone Number.  Only supports
// US numbers currently.
func ToPhone(theString string, delimiter string) (string, error) {
	if len(theString) < 10 {
		return "", errors.New(lengthError)
	}

	f := New(theString)
	f.removeNonDigits()

	var buffer bytes.Buffer
	count := 0

	if len(f.theString) == 10 {
		buffer.WriteString("(")
		for _, i := range f.theString {
			count++

			buffer.WriteString(string(i))

			if count == 3 {
				buffer.WriteString(") ")
			} else if count == 6 {
				buffer.WriteString(fmt.Sprintf("%s", delimiter))
			}
		}
	} else if len(f.theString) == 11 {
		for _, i := range f.theString {
			count++

			buffer.WriteString(string(i))

			if count == 1 {
				buffer.WriteString(" (")
			} else if count == 4 {
				buffer.WriteString(") ")
			} else if count == 7 {
				buffer.WriteString(fmt.Sprintf("%s", delimiter))
			}
		}
	}
	return buffer.String(), nil
}

// ToURL will format the provided string as a URL.  HTTP and HTTPS
// are the only supported protocols at this time.
func ToURL(theString string, secure bool, subdomain string) (string, error) {
	// I'm assuming we have at least a 1 character domain, a dot, and a two
	// char TLD.
	if len(theString) < 4 {
		return "", errors.New(lengthError)
	}

	f := New(theString)

	f.makeLower()

	if secure {
		if len(subdomain) > 0 {
			return fmt.Sprintf("https://%s.%s", subdomain, f.theString), nil
		}
		return fmt.Sprintf("https://%s", f.theString), nil
	}

	if len(subdomain) > 0 {
		return fmt.Sprintf("http://%s.%s", subdomain, f.theString), nil
	}
	return fmt.Sprintf("http://%s", f.theString), nil
}

// ToSSN will format the provided string as a SSN.
func ToSSN(theString string, delimiter string) (string, error) {
	f := New(theString)

	f.removeNonDigits()

	if len(f.theString) != 9 {
		return "", errors.New("ERROR: String needs to be 9 digits for Social Security Numbers")
	}

	var buffer bytes.Buffer
	count := 0

	for _, i := range f.theString {
		count++

		buffer.WriteString(string(i))

		if count == 3 || count == 5 {
			buffer.WriteString(delimiter)
		}
	}
	return buffer.String(), nil
}

// ToLockCombo will format the provided string as a Lock Combo.
func ToLockCombo(theString string, delimiter string) (string, error) {
	f := New(theString)

	f.removeNonDigits()

	if len(f.theString) != 9 {
		return "", errors.New("ERROR: String needs to be 6 digits for Lock Combo format")
	}

	var buffer bytes.Buffer
	count := 0

	for _, i := range f.theString {
		count++

		buffer.WriteString(string(i))

		if count == 2 || count == 4 {
			buffer.WriteString(delimiter)
		}
	}
	return buffer.String(), nil
}

// ToISBN will format the provided string in International Standard Book Number
// (ISBN) format.
func ToISBN(theString string, delimiter string) (string, error) {
	f := New(theString)

	f.removeNonDigits()

	if len(f.theString) != 13 {
		return "", errors.New("ERROR: string must be 13 characters")
	}

	var buffer bytes.Buffer
	count := 0

	for _, i := range f.theString {
		count++

		buffer.WriteString(string(i))

		if count == 3 || count == 4 || count == 6 || count == 12 {
			buffer.WriteString(delimiter)
		}
	}
	return buffer.String(), nil
}

// ToMorseCode will format the provided string in Morse Code.
func ToMorseCode(theString string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.makeLower()

	var buffer bytes.Buffer

	for _, i := range f.theString {
		key := string(i)
		if _, ok := morse[key]; ok {
			buffer.WriteString(morse[key])
		} else if key == " " {
			buffer.WriteString(" ")
		}
	}
	return buffer.String(), nil
}

// ToPirateSpeak will format the provided string in Pirate Speak.
func ToPirateSpeak(theString string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.urlEncodeSpaces()

	response, err := http.Get(fmt.Sprintf(pirateLink, f.theString))
	if err != nil {
		return "", errors.New("ERROR: Unable to convert")
	}
	defer response.Body.Close()

	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", err
	}
	return string(contents), nil
}

// ToIRSA will format the provided string in IRSA.
// International Radio-Telephony Spelling Alphabet
func ToIRSA(theString string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.makeLower()

	var buffer bytes.Buffer

	for _, i := range f.theString {
		key := strings.ToLower(string(i))
		if _, ok := irsa[key]; ok {
			buffer.WriteString(irsa[key] + " ")
		}
	}
	return buffer.String(), nil
}

// ToLeet will format the provided string in Leet Speak.
// TODO(bdowns328) Make select post match random.
func ToLeet(theString string) (string, error) {
	f := New(theString)

	if len(f.theString) < 1 {
		return "", errors.New(lengthError)
	}

	f.makeLower()

	words := strings.Fields(f.theString)
	var buffer bytes.Buffer

	for _, word := range words {
		key := string(word)
		if _, ok := leet[word]; ok {
			buffer.WriteString(leet[key][0] + " ")
		} else {
			for _, i := range word {
				letter := string(i)
				if _, ok := leet[letter]; ok {
					buffer.WriteString(leet[letter][0])
				}
			}
			buffer.WriteString(" ")
		}
	}
	return buffer.String(), nil
}
<|endoftext|>


<commit_before>package appdeploy

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"time"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/errors"
	"k8s.io/client-go/pkg/api/v1"
	meta_v1 "k8s.io/client-go/pkg/apis/meta/v1"
	"k8s.io/client-go/rest"

	"github.com/eapache/go-resiliency/retrier"
	"github.com/rubenv/kube-appdeploy/kubectl"
)

var CleanTypes = []string{
	"deployment",
	"service",
	"cronjob",
}

type Target interface {
	Prepare(vars *ProcessVariables) error
	Apply(m Manifest, data []byte) error
	Cleanup(items []Manifest) error
}

// ---------- Folder ----------

type FolderTarget struct {
	Path string
}

var _ Target = &FolderTarget{}

func NewFolderTarget(path string) *FolderTarget {
	return &FolderTarget{
		Path: path,
	}
}

func (t *FolderTarget) Prepare(vars *ProcessVariables) error {
	return os.MkdirAll(t.Path, 0755)
}

func (t *FolderTarget) Apply(m Manifest, data []byte) error {
	return ioutil.WriteFile(m.Filename(t.Path), data, 0644)
}

func (t *FolderTarget) Cleanup(items []Manifest) error {
	files, err := ioutil.ReadDir(t.Path)
	if err != nil {
		return err
	}

	filenames := make([]string, 0, len(items))
	for _, item := range items {
		filenames = append(filenames, item.Filename(""))
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}

		name := file.Name()
		sep := strings.Index(name, "--")
		if sep < 0 {
			continue
		}

		prefix := name[0:sep]
		found := false
		for _, t := range CleanTypes {
			if t == prefix {
				found = true
			}
		}
		if !found {
			continue
		}

		known := false
		for _, f := range filenames {
			if f == name {
				known = true
			}
		}

		if !known {
			err = os.Remove(path.Join(t.Path, name))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// ---------- Kubernetes ----------

type KubernetesTarget struct {
	config    *rest.Config
	client    *kubernetes.Clientset
	kubectl   *kubectl.KubeCtl
	namespace string
}

var _ Target = &KubernetesTarget{}

func NewKubernetesTarget(config *rest.Config) *KubernetesTarget {
	return &KubernetesTarget{
		config: config,
	}
}

func (t *KubernetesTarget) Prepare(vars *ProcessVariables) error {
	client, err := kubernetes.NewForConfig(t.config)
	if err != nil {
		return err
	}

	t.client = client

	// Copy some vars
	t.namespace = vars.Namespace
	t.kubectl = kubectl.NewKubeCtl(t.config, t.namespace)

	// Ensure we have the needed namespace
	nsClient := t.client.Core().Namespaces()

	create := false
	_, err = nsClient.Get(t.namespace, meta_v1.GetOptions{})
	if err != nil {
		ignore := false
		if e, ok := err.(*errors.StatusError); ok {
			if e.ErrStatus.Reason == "NotFound" {
				ignore = true
				create = true
			}
		}
		if !ignore {
			return err
		}
	}
	if create {
		_, err = nsClient.Create(&v1.Namespace{
			ObjectMeta: v1.ObjectMeta{
				Name: t.namespace,
			},
		})
		if err != nil {
			return err
		}
	}

	// Add the image pull secrets
	if len(vars.ImagePullSecrets) > 0 {
		saClient := t.client.Core().ServiceAccounts(t.namespace)

		var sa *v1.ServiceAccount
		// Account isn't always available right away, but it gets created in the end, just wait for it
		r := retrier.New(retrier.ConstantBackoff(10, 1*time.Second), nil)
		err := r.Run(func() error {
			s, err := saClient.Get("default", meta_v1.GetOptions{})
			if err != nil {
				return err
			}
			sa = s
			return nil
		})

		secrets := make([]v1.LocalObjectReference, 0)
		for _, s := range vars.ImagePullSecrets {
			secrets = append(secrets, v1.LocalObjectReference{
				Name: s,
			})
		}

		sa.ImagePullSecrets = secrets
		_, err = saClient.Update(sa)
		if err != nil {
			return err
		}
	}

	return nil
}

func (t *KubernetesTarget) Apply(m Manifest, data []byte) error {
	_, err := t.runKubeCtl(data, "apply", "-f", "-")
	return err
}

func (t *KubernetesTarget) Cleanup(items []Manifest) error {
	for _, ct := range CleanTypes {
		err := t.cleanType(items, ct)
		if err != nil {
			return err
		}
	}
	return nil
}

func (t *KubernetesTarget) cleanType(items []Manifest, ct string) error {
	out, err := t.runKubeCtl(nil, "get", ct, "-o", "name")
	if err != nil {
		return err
	}

	known := []string{}
	for _, m := range items {
		if strings.ToLower(m.Kind) == ct {
			known = append(known, fmt.Sprintf("%s/%s", ct, m.Metadata.Name))
		}
	}

	lines := strings.Split(strings.TrimSpace(out), "\n")
	for _, line := range lines {
		if line == "" {
			continue
		}

		found := false
		for _, k := range known {
			if line == k {
				found = true
			}
		}

		if !found {
			_, err := t.runKubeCtl(nil, "delete", line)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (t *KubernetesTarget) runKubeCtl(stdin []byte, args ...string) (string, error) {
	return t.kubectl.Run(stdin, args...)
}
<commit_msg>Workaround for error on apply of cronjob<commit_after>package appdeploy

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"time"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/errors"
	"k8s.io/client-go/pkg/api/v1"
	meta_v1 "k8s.io/client-go/pkg/apis/meta/v1"
	"k8s.io/client-go/rest"

	"github.com/eapache/go-resiliency/retrier"
	"github.com/rubenv/kube-appdeploy/kubectl"
)

var CleanTypes = []string{
	"deployment",
	"service",
	"cronjob",
}

type Target interface {
	Prepare(vars *ProcessVariables) error
	Apply(m Manifest, data []byte) error
	Cleanup(items []Manifest) error
}

// ---------- Folder ----------

type FolderTarget struct {
	Path string
}

var _ Target = &FolderTarget{}

func NewFolderTarget(path string) *FolderTarget {
	return &FolderTarget{
		Path: path,
	}
}

func (t *FolderTarget) Prepare(vars *ProcessVariables) error {
	return os.MkdirAll(t.Path, 0755)
}

func (t *FolderTarget) Apply(m Manifest, data []byte) error {
	return ioutil.WriteFile(m.Filename(t.Path), data, 0644)
}

func (t *FolderTarget) Cleanup(items []Manifest) error {
	files, err := ioutil.ReadDir(t.Path)
	if err != nil {
		return err
	}

	filenames := make([]string, 0, len(items))
	for _, item := range items {
		filenames = append(filenames, item.Filename(""))
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}

		name := file.Name()
		sep := strings.Index(name, "--")
		if sep < 0 {
			continue
		}

		prefix := name[0:sep]
		found := false
		for _, t := range CleanTypes {
			if t == prefix {
				found = true
			}
		}
		if !found {
			continue
		}

		known := false
		for _, f := range filenames {
			if f == name {
				known = true
			}
		}

		if !known {
			err = os.Remove(path.Join(t.Path, name))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// ---------- Kubernetes ----------

type KubernetesTarget struct {
	config    *rest.Config
	client    *kubernetes.Clientset
	kubectl   *kubectl.KubeCtl
	namespace string
}

var _ Target = &KubernetesTarget{}

func NewKubernetesTarget(config *rest.Config) *KubernetesTarget {
	return &KubernetesTarget{
		config: config,
	}
}

func (t *KubernetesTarget) Prepare(vars *ProcessVariables) error {
	client, err := kubernetes.NewForConfig(t.config)
	if err != nil {
		return err
	}

	t.client = client

	// Copy some vars
	t.namespace = vars.Namespace
	t.kubectl = kubectl.NewKubeCtl(t.config, t.namespace)

	// Ensure we have the needed namespace
	nsClient := t.client.Core().Namespaces()

	create := false
	_, err = nsClient.Get(t.namespace, meta_v1.GetOptions{})
	if err != nil {
		ignore := false
		if e, ok := err.(*errors.StatusError); ok {
			if e.ErrStatus.Reason == "NotFound" {
				ignore = true
				create = true
			}
		}
		if !ignore {
			return err
		}
	}
	if create {
		_, err = nsClient.Create(&v1.Namespace{
			ObjectMeta: v1.ObjectMeta{
				Name: t.namespace,
			},
		})
		if err != nil {
			return err
		}
	}

	// Add the image pull secrets
	if len(vars.ImagePullSecrets) > 0 {
		saClient := t.client.Core().ServiceAccounts(t.namespace)

		var sa *v1.ServiceAccount
		// Account isn't always available right away, but it gets created in the end, just wait for it
		r := retrier.New(retrier.ConstantBackoff(10, 1*time.Second), nil)
		err := r.Run(func() error {
			s, err := saClient.Get("default", meta_v1.GetOptions{})
			if err != nil {
				return err
			}
			sa = s
			return nil
		})

		secrets := make([]v1.LocalObjectReference, 0)
		for _, s := range vars.ImagePullSecrets {
			secrets = append(secrets, v1.LocalObjectReference{
				Name: s,
			})
		}

		sa.ImagePullSecrets = secrets
		_, err = saClient.Update(sa)
		if err != nil {
			return err
		}
	}

	return nil
}

func (t *KubernetesTarget) Apply(m Manifest, data []byte) error {
	// Temporary fix for https://github.com/kubernetes/kubernetes/issues/35149
	// If a cronjob is applied, an error occurs
	// Thus we delete the cronjob first if it exists
	if m.Kind == "CronJob" {
		out, err := t.runKubeCtl(nil, "get", "cronjob", "-o", "name")
		if err != nil {
			return err
		}
		lines := strings.Split(strings.TrimSpace(out), "\n")
		searchline := fmt.Sprintf("cronjob/%s", m.Metadata.Name)
		for _, line := range lines {
			if line == searchline {
				_, err := t.runKubeCtl(nil, "delete", "cronjob", m.Metadata.Name)
				if err != nil {
					return err
				}
				break
			}
		}
	}
	_, err := t.runKubeCtl(data, "apply", "-f", "-")
	return err
}

func (t *KubernetesTarget) Cleanup(items []Manifest) error {
	for _, ct := range CleanTypes {
		err := t.cleanType(items, ct)
		if err != nil {
			return err
		}
	}
	return nil
}

func (t *KubernetesTarget) cleanType(items []Manifest, ct string) error {
	out, err := t.runKubeCtl(nil, "get", ct, "-o", "name")
	if err != nil {
		return err
	}

	known := []string{}
	for _, m := range items {
		if strings.ToLower(m.Kind) == ct {
			known = append(known, fmt.Sprintf("%s/%s", ct, m.Metadata.Name))
		}
	}

	lines := strings.Split(strings.TrimSpace(out), "\n")
	for _, line := range lines {
		if line == "" {
			continue
		}

		found := false
		for _, k := range known {
			if line == k {
				found = true
			}
		}

		if !found {
			_, err := t.runKubeCtl(nil, "delete", line)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (t *KubernetesTarget) runKubeCtl(stdin []byte, args ...string) (string, error) {
	return t.kubectl.Run(stdin, args...)
}
<|endoftext|>