<commit_before>package sdl

import (
	"bytes"
	"testing"
)

var squareWave = []byte("RIFF,\x00\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00" +
	"\x01\x00\xab \x00\x00\xab \x00\x00\x01\x00\b\x00data\b\x00\x00\x00\x00" +
	"\x00\x00\x00\xff\xff\xff\xff")

func TestAudioDevices(t *testing.T) {
	for _, isCapture := range []bool{false, true} {
		n := GetNumAudioDevices(isCapture)
		for i := 0; i < n; i++ {
			if GetAudioDeviceName(i, isCapture) == "" {
				t.Errorf("GetAudioDeviceName(%v, %v) returned empty string",
					i, isCapture)
			}
		}

		// cover GetAudioDeviceName() even if n < 1
		if name := GetAudioDeviceName(-1, isCapture); name != "" {
			t.Errorf("GetAudioDeviceName(-1, %v) == %#v; want empty string",
				isCapture, name)
		}
	}
}

func TestAudioInitQuit(t *testing.T) {
	Do(func() {
		// figure out what driver will work
		if err := InitSubSystem(INIT_AUDIO); err != nil {
			t.Fatalf("InitSubSystem(INIT_AUDIO): %v", err)
		}
		driver := GetCurrentAudioDriver()

		// reset audio subsystem
		QuitSubSystem(INIT_AUDIO)
		if GetCurrentAudioDriver() != "" {
			t.Fatalf(`GetCurrentAudioDriver() != "" after QuitSubSystem()`)
		}

		// test valid init
		if err := AudioInit(driver); err != nil {
			t.Errorf("AudioInit(%s) returned error: %v", driver, err)
		} else {
			if got := GetCurrentAudioDriver(); got != driver {
				t.Errorf("GetCurrentAudioDriver() == %#v; want %#v", got, driver)
			}

			// test quit
			AudioQuit()
			if GetCurrentAudioDriver() != "" {
				t.Fatalf(`GetCurrentAudioDriver() != "" after AudioQuit()`)
			}
		}

		// test invalid init
		driver = "bogus"
		if err := AudioInit(driver); err == nil {
			t.Errorf("AudioInit(%s) did not return error", driver)
		}
	})
}

func TestLoadWAVRW(t *testing.T) {
	// load WAV from *RWOps pointing to WAV data
	src, err := RWFromMem(squareWave)
	if err != nil {
		t.Errorf("LoadWAVRW() returned error %v", err)
	}
	buf, spec := LoadWAVRW(src, false)

	// test returned []byte
	want := []byte{0, 0, 0, 0, 255, 255, 255, 255}
	if buf == nil {
		t.Errorf("LoadWAVRW() returned nil []byte")
	} else {
		if bytes.Compare(buf, want) != 0 {
			t.Errorf("LoadWAVRW() returned %v; want %v", buf, want)
		}
		FreeWAV(buf) // make sure we can free without error
	}

	// test returned *AudioSpec
	if spec == nil {
		t.Errorf("LoadWAVRW() returned nil *AudioSpec")
	} else if spec.Freq != 8363 { // no need to test all the spec fields
		t.Errorf("LoadWAVRW() returned *AudioSpec with Freq %d; want %d",
			spec.Freq, 8363)
	}
}
<commit_msg>fix RWFromMem of sdl/audio_test.go (#376)<commit_after>package sdl

import (
	"bytes"
	"testing"
)

var squareWave = []byte("RIFF,\x00\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00" +
	"\x01\x00\xab \x00\x00\xab \x00\x00\x01\x00\b\x00data\b\x00\x00\x00\x00" +
	"\x00\x00\x00\xff\xff\xff\xff")

func TestAudioDevices(t *testing.T) {
	for _, isCapture := range []bool{false, true} {
		n := GetNumAudioDevices(isCapture)
		for i := 0; i < n; i++ {
			if GetAudioDeviceName(i, isCapture) == "" {
				t.Errorf("GetAudioDeviceName(%v, %v) returned empty string",
					i, isCapture)
			}
		}

		// cover GetAudioDeviceName() even if n < 1
		if name := GetAudioDeviceName(-1, isCapture); name != "" {
			t.Errorf("GetAudioDeviceName(-1, %v) == %#v; want empty string",
				isCapture, name)
		}
	}
}

func TestAudioInitQuit(t *testing.T) {
	Do(func() {
		// figure out what driver will work
		if err := InitSubSystem(INIT_AUDIO); err != nil {
			t.Fatalf("InitSubSystem(INIT_AUDIO): %v", err)
		}
		driver := GetCurrentAudioDriver()

		// reset audio subsystem
		QuitSubSystem(INIT_AUDIO)
		if GetCurrentAudioDriver() != "" {
			t.Fatalf(`GetCurrentAudioDriver() != "" after QuitSubSystem()`)
		}

		// test valid init
		if err := AudioInit(driver); err != nil {
			t.Errorf("AudioInit(%s) returned error: %v", driver, err)
		} else {
			if got := GetCurrentAudioDriver(); got != driver {
				t.Errorf("GetCurrentAudioDriver() == %#v; want %#v", got, driver)
			}

			// test quit
			AudioQuit()
			if GetCurrentAudioDriver() != "" {
				t.Fatalf(`GetCurrentAudioDriver() != "" after AudioQuit()`)
			}
		}

		// test invalid init
		driver = "bogus"
		if err := AudioInit(driver); err == nil {
			t.Errorf("AudioInit(%s) did not return error", driver)
		}
	})
}

func TestLoadWAVRW(t *testing.T) {
	// load WAV from *RWOps pointing to WAV data
	src, _ := RWFromMem(squareWave)
	buf, spec := LoadWAVRW(src, false)

	// test returned []byte
	want := []byte{0, 0, 0, 0, 255, 255, 255, 255}
	if buf == nil {
		t.Errorf("LoadWAVRW() returned nil []byte")
	} else {
		if bytes.Compare(buf, want) != 0 {
			t.Errorf("LoadWAVRW() returned %v; want %v", buf, want)
		}
		FreeWAV(buf) // make sure we can free without error
	}

	// test returned *AudioSpec
	if spec == nil {
		t.Errorf("LoadWAVRW() returned nil *AudioSpec")
	} else if spec.Freq != 8363 { // no need to test all the spec fields
		t.Errorf("LoadWAVRW() returned *AudioSpec with Freq %d; want %d",
			spec.Freq, 8363)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Rook Authors. All rights reserved.

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 cluster to manage a Ceph cluster.
package cluster

import (
	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
	cephclient "github.com/rook/rook/pkg/daemon/ceph/client"
	clientcontroller "github.com/rook/rook/pkg/operator/ceph/client"
	"github.com/rook/rook/pkg/operator/ceph/cluster/mon"
	"github.com/rook/rook/pkg/operator/ceph/cluster/osd"
	"github.com/rook/rook/pkg/operator/ceph/object/bucket"
)

func (c *ClusterController) configureCephMonitoring(cluster *cluster, clusterInfo *cephclient.ClusterInfo) {
	var isDisabled bool
	daemons := []string{"mon", "osd", "status"}

	for _, daemon := range daemons {
		// Is the monitoring enabled for that daemon?
		isDisabled = isMonitoringDisabled(daemon, cluster.Spec)
		if health, ok := cluster.monitoringChannels[daemon]; ok {
			if health.monitoringRunning {
				// If the goroutine was running but the CR was updated to stop the monitoring we need to close the channel
				if isDisabled {
					// close the channel so the goroutine can stop
					close(cluster.monitoringChannels[daemon].stopChan)
					// Set monitoring to false since it's not running anymore
					cluster.monitoringChannels[daemon].monitoringRunning = false
				} else {
					logger.Debugf("ceph %s health go routine is already running for cluster %q", daemon, cluster.Namespace)
				}
			} else {
				// if not already running and not disabled, we run it
				if !isDisabled {
					// Run the go routine
					c.startMonitoringCheck(cluster, clusterInfo, daemon)

					// Set the flag to indicate monitoring is running
					cluster.monitoringChannels[daemon].monitoringRunning = true
				}
			}
		} else {
			// If the mon does not exist in the map, this is a first deployment or an operator restart
			// So we check the desired state from the CR and run it if necessary
			//
			// If the mon monitoring is enabled
			if !isDisabled {
				cluster.monitoringChannels[daemon] = &clusterHealth{
					stopChan:          make(chan struct{}),
					monitoringRunning: true, // Set the flag to indicate monitoring is running
				}

				// Run the go routine
				c.startMonitoringCheck(cluster, clusterInfo, daemon)
			}
		}
	}

	// Start watchers
	if cluster.watchersActivated == true {
		logger.Debugf("cluster is already being watched by bucket and client provisioner for cluster %q", cluster.Namespace)
		return
	}

	// Start client CRD watcher
	cc := clientcontroller.NewClientController(c.context, cluster.Namespace)
	cc.StartWatch(cluster.stopCh)

	// Start the object bucket provisioner
	bucketProvisioner := bucket.NewProvisioner(c.context, clusterInfo)
	// If cluster is external, pass down the user to the bucket controller

	// note: the error return below is ignored and is expected to be removed from the
	//   bucket library's `NewProvisioner` function
	bucketController, _ := bucket.NewBucketController(c.context.KubeConfig, bucketProvisioner)
	go bucketController.Run(cluster.stopCh)

	// enable the cluster watcher once
	cluster.watchersActivated = true
}

func isMonitoringDisabled(daemon string, clusterSpec *cephv1.ClusterSpec) bool {
	switch daemon {
	case "mon":
		return clusterSpec.HealthCheck.DaemonHealth.Monitor.Disabled

	case "osd":
		return clusterSpec.HealthCheck.DaemonHealth.ObjectStorageDaemon.Disabled

	case "status":
		return clusterSpec.HealthCheck.DaemonHealth.Status.Disabled
	}

	return false
}

func (c *ClusterController) startMonitoringCheck(cluster *cluster, clusterInfo *cephclient.ClusterInfo, daemon string) {
	switch daemon {
	case "mon":
		healthChecker := mon.NewHealthChecker(cluster.mons, cluster.Spec)
		logger.Infof("enabling ceph %s monitoring goroutine for cluster %q", daemon, cluster.Namespace)
		go healthChecker.Check(cluster.monitoringChannels[daemon].stopChan)

	case "osd":
		if !cluster.Spec.External.Enable {
			c.osdChecker = osd.NewOSDHealthMonitor(c.context, clusterInfo, cluster.Spec.RemoveOSDsIfOutAndSafeToRemove, cluster.Spec.HealthCheck)
			logger.Infof("enabling ceph %s monitoring goroutine for cluster %q", daemon, cluster.Namespace)
			go c.osdChecker.Start(cluster.monitoringChannels[daemon].stopChan)
		}

	case "status":
		cephChecker := newCephStatusChecker(c.context, clusterInfo, cluster.Spec.HealthCheck)
		logger.Infof("enabling ceph %s monitoring goroutine for cluster %q", daemon, cluster.Namespace)
		go cephChecker.checkCephStatus(cluster.monitoringChannels[daemon].stopChan)
	}
}
<commit_msg>ceph: remove unnecessary comment<commit_after>/*
Copyright 2020 The Rook Authors. All rights reserved.

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 cluster to manage a Ceph cluster.
package cluster

import (
	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
	cephclient "github.com/rook/rook/pkg/daemon/ceph/client"
	clientcontroller "github.com/rook/rook/pkg/operator/ceph/client"
	"github.com/rook/rook/pkg/operator/ceph/cluster/mon"
	"github.com/rook/rook/pkg/operator/ceph/cluster/osd"
	"github.com/rook/rook/pkg/operator/ceph/object/bucket"
)

func (c *ClusterController) configureCephMonitoring(cluster *cluster, clusterInfo *cephclient.ClusterInfo) {
	var isDisabled bool
	daemons := []string{"mon", "osd", "status"}

	for _, daemon := range daemons {
		// Is the monitoring enabled for that daemon?
		isDisabled = isMonitoringDisabled(daemon, cluster.Spec)
		if health, ok := cluster.monitoringChannels[daemon]; ok {
			if health.monitoringRunning {
				// If the goroutine was running but the CR was updated to stop the monitoring we need to close the channel
				if isDisabled {
					// close the channel so the goroutine can stop
					close(cluster.monitoringChannels[daemon].stopChan)
					// Set monitoring to false since it's not running anymore
					cluster.monitoringChannels[daemon].monitoringRunning = false
				} else {
					logger.Debugf("ceph %s health go routine is already running for cluster %q", daemon, cluster.Namespace)
				}
			} else {
				// if not already running and not disabled, we run it
				if !isDisabled {
					// Run the go routine
					c.startMonitoringCheck(cluster, clusterInfo, daemon)

					// Set the flag to indicate monitoring is running
					cluster.monitoringChannels[daemon].monitoringRunning = true
				}
			}
		} else {
			if !isDisabled {
				cluster.monitoringChannels[daemon] = &clusterHealth{
					stopChan:          make(chan struct{}),
					monitoringRunning: true, // Set the flag to indicate monitoring is running
				}

				// Run the go routine
				c.startMonitoringCheck(cluster, clusterInfo, daemon)
			}
		}
	}

	// Start watchers
	if cluster.watchersActivated == true {
		logger.Debugf("cluster is already being watched by bucket and client provisioner for cluster %q", cluster.Namespace)
		return
	}

	// Start client CRD watcher
	cc := clientcontroller.NewClientController(c.context, cluster.Namespace)
	cc.StartWatch(cluster.stopCh)

	// Start the object bucket provisioner
	bucketProvisioner := bucket.NewProvisioner(c.context, clusterInfo)
	// If cluster is external, pass down the user to the bucket controller

	// note: the error return below is ignored and is expected to be removed from the
	//   bucket library's `NewProvisioner` function
	bucketController, _ := bucket.NewBucketController(c.context.KubeConfig, bucketProvisioner)
	go bucketController.Run(cluster.stopCh)

	// enable the cluster watcher once
	cluster.watchersActivated = true
}

func isMonitoringDisabled(daemon string, clusterSpec *cephv1.ClusterSpec) bool {
	switch daemon {
	case "mon":
		return clusterSpec.HealthCheck.DaemonHealth.Monitor.Disabled

	case "osd":
		return clusterSpec.HealthCheck.DaemonHealth.ObjectStorageDaemon.Disabled

	case "status":
		return clusterSpec.HealthCheck.DaemonHealth.Status.Disabled
	}

	return false
}

func (c *ClusterController) startMonitoringCheck(cluster *cluster, clusterInfo *cephclient.ClusterInfo, daemon string) {
	switch daemon {
	case "mon":
		healthChecker := mon.NewHealthChecker(cluster.mons, cluster.Spec)
		logger.Infof("enabling ceph %s monitoring goroutine for cluster %q", daemon, cluster.Namespace)
		go healthChecker.Check(cluster.monitoringChannels[daemon].stopChan)

	case "osd":
		if !cluster.Spec.External.Enable {
			c.osdChecker = osd.NewOSDHealthMonitor(c.context, clusterInfo, cluster.Spec.RemoveOSDsIfOutAndSafeToRemove, cluster.Spec.HealthCheck)
			logger.Infof("enabling ceph %s monitoring goroutine for cluster %q", daemon, cluster.Namespace)
			go c.osdChecker.Start(cluster.monitoringChannels[daemon].stopChan)
		}

	case "status":
		cephChecker := newCephStatusChecker(c.context, clusterInfo, cluster.Spec.HealthCheck)
		logger.Infof("enabling ceph %s monitoring goroutine for cluster %q", daemon, cluster.Namespace)
		go cephChecker.checkCephStatus(cluster.monitoringChannels[daemon].stopChan)
	}
}
<|endoftext|>


<commit_before>// Package httpclient is a lightweight implementation of the HTTP gateway.
package httpclient

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
)

var defaultHttpClient = http.Client{}
var defaultJsonPbMarshaller = jsonpb.Marshaler{}

func toJson(dst io.Writer, m proto.Message) error {
	return defaultJsonPbMarshaller.Marshal(dst, m)
}

func fromJson(src io.Reader, dst proto.Message) error {
	return jsonpb.Unmarshal(src, dst)
}

func call(method string, url string, in proto.Message, out proto.Message) error {
	buf := bytes.NewBuffer(nil)
	if in != nil {
		err := toJson(buf, in)
		if err != nil {
			return err
		}
	}
	req, err := http.NewRequest(method, url, buf)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := defaultHttpClient.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode >= 400 {
		data, _ := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		return fmt.Errorf("request failed - %s: %s", resp.Status, strings.TrimSpace(string(data)))
	}

	if out != nil {
		err = fromJson(resp.Body, out)
		if err != nil {
			return err
		}
		err = resp.Body.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

type BaseApi struct {
	endpoint string
	client   http.Client
}

// TODO remove hard-coded proxy
func (api *BaseApi) formatUrl(path string) string {
	return api.endpoint + "/proxy/workflows-apiserver" + path
}
<commit_msg>Fixed bug in httpclient: crashing on empty response body<commit_after>// Package httpclient is a lightweight implementation of the HTTP gateway.
package httpclient

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

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/sirupsen/logrus"
)

var (
	ErrResponseError = errors.New("response error")
	ErrRequestCreate = errors.New("failed to create request")
	ErrRequestSend   = errors.New("failed to send request")
	ErrSerialize     = errors.New("failed to serialize input")
	ErrDeserialize   = errors.New("failed to deserialize input")
)

var defaultHttpClient = http.Client{}
var defaultJsonPbMarshaller = jsonpb.Marshaler{}

func toJson(dst io.Writer, m proto.Message) error {
	return defaultJsonPbMarshaller.Marshal(dst, m)
}

func fromJson(src io.Reader, dst proto.Message) error {
	return jsonpb.Unmarshal(src, dst)
}

func call(method string, url string, in proto.Message, out proto.Message) error {
	buf := bytes.NewBuffer(nil)
	if in != nil {
		err := toJson(buf, in)
		if err != nil {
			return fmt.Errorf("%v: %v", ErrSerialize, err)
		}
	}
	if logrus.GetLevel() == logrus.DebugLevel {
		logrus.Debugf("--> %s - %s", method, url)
		if in != nil {
			buf := bytes.NewBuffer(nil)
			err := toJson(buf, in)
			if err != nil {
				logrus.Errorf("Failed to jsonify debug data: %v", err)
			}
			data, err := ioutil.ReadAll(buf)
			if err != nil {
				logrus.Errorf("Failed to read debug data: %v", err)
			}
			logrus.Debug("body: '%v'", data)
		}
	}
	req, err := http.NewRequest(method, url, buf)
	if err != nil {
		return fmt.Errorf("%v: %v", ErrRequestCreate, err)
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := defaultHttpClient.Do(req)
	if err != nil {
		return fmt.Errorf("%v: %v", ErrRequestSend, err)
	}
	if logrus.GetLevel() == logrus.DebugLevel {
		logrus.Debugf("<-- %s - %s", resp.Status, url)
		if resp.Body != nil {
			data, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				logrus.Errorf("Failed to read debug body", err)
			}
			logrus.Debugf("Body: '%v'", string(data))
		}
	}
	if resp.StatusCode >= 400 {
		data, _ := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		return fmt.Errorf("%v (%s): %s", ErrResponseError, resp.Status, strings.TrimSpace(string(data)))
	}

	if out != nil && resp.ContentLength != 0 {

		err = fromJson(resp.Body, out)
		if err != nil {
			return fmt.Errorf("%v: %v", ErrDeserialize, err)
		}
		resp.Body.Close()
		if err != nil {
			logrus.Errorf("Failed to close response body: %v", err)
		}
	}
	return nil
}

type BaseApi struct {
	endpoint string
	client   http.Client
}

// TODO remove hard-coded proxy
func (api *BaseApi) formatUrl(path string) string {
	return api.endpoint + "/proxy/workflows-apiserver" + path
}
<|endoftext|>


<commit_before>package content

import (
	"text/template"
)

const (
	// BootkubeSystemdContents is a service for running bootkube on the bootstrap
	// nodes
	BootkubeSystemdContents = `[Unit]
Description=Bootstrap a Kubernetes cluster
Wants=kubelet.service
After=kubelet.service

[Service]
WorkingDirectory=/opt/tectonic

ExecStart=/opt/tectonic/bootkube.sh

Restart=on-failure
RestartSec=5s`
)

var (
	// BootkubeShFileTemplate is a script file for running bootkube on the
	// bootstrap nodes.
	BootkubeShFileTemplate = template.Must(template.New("bootkube.sh").Parse(`#!/usr/bin/env bash
set -e

mkdir --parents /etc/kubernetes/manifests/

MACHINE_CONFIG_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-operator)
MACHINE_CONFIG_CONTROLLER_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-controller)
MACHINE_CONFIG_SERVER_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-server)
MACHINE_CONFIG_DAEMON_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-daemon)

KUBE_APISERVER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-apiserver-operator)
KUBE_CONTROLLER_MANAGER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-controller-manager-operator)
KUBE_SCHEDULER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-scheduler-operator)

if [ ! -d cvo-bootstrap ]
then
	echo "Rendering Cluster Version Operator Manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"{{.ReleaseImage}}" \
		render \
			--output-dir=/assets/cvo-bootstrap \
			--release-image="{{.ReleaseImage}}"

	cp --recursive cvo-bootstrap/manifests .
	cp --recursive cvo-bootstrap/bootstrap/bootstrap-pod.yaml /etc/kubernetes/manifests/
fi

if [ ! -d kco-bootstrap ]
then
	echo "Rendering Kubernetes core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		--volume /etc/kubernetes:/etc/kubernetes:z \
		"{{.KubeCoreRenderImage}}" \
		--config=/assets/kco-config.yaml \
		--output=/assets/kco-bootstrap

	cp --recursive kco-bootstrap/bootstrap-configs /etc/kubernetes/bootstrap-configs
	cp --recursive kco-bootstrap/bootstrap-manifests .
	cp --recursive kco-bootstrap/manifests .
fi

if [ ! -d kube-apiserver-bootstrap ]
then
	echo "Rendering Kubernetes API server core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_APISERVER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-apiserver-operator render \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-apiserver-bootstrap \
		--config-override-file=/usr/share/bootkube/manifests/config/config-overrides.yaml \
		--config-output-file=/assets/kube-apiserver-bootstrap/config

	# TODO: copy the bootstrap manifests to replace kube-core-operator
	cp --recursive kube-apiserver-bootstrap/manifests/00_openshift-kube-apiserver-ns.yaml manifests/00_openshift-kube-apiserver-ns.yaml
	cp --recursive kube-apiserver-bootstrap/manifests/secret-* manifests/
	cp --recursive kube-apiserver-bootstrap/manifests/configmap-* manifests/
fi

if [ ! -d kube-controller-manager-bootstrap ]
then
	echo "Rendering Kubernetes Controller Manager core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_CONTROLLER_MANAGER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-controller-manager-operator render \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-controller-manager-bootstrap \
		--config-override-file=/usr/share/bootkube/manifests/config/config-overrides.yaml \
		--config-output-file=/assets/kube-controller-manager-bootstrap/config

	# TODO: copy the bootstrap manifests to replace kube-core-operator
	cp --recursive kube-controller-manager-bootstrap/manifests/00_openshift-kube-controller-manager-ns.yaml manifests/00_openshift-kube-controller-manager-ns.yaml
	cp --recursive kube-controller-manager-bootstrap/manifests/secret-* manifests/
	cp --recursive kube-controller-manager-bootstrap/manifests/configmap-* manifests/
fi

if [ ! -d kube-scheduler-bootstrap ]
then
        echo "Rendering Kubernetes Scheduler core manifests..."

        # shellcheck disable=SC2154
        podman run \
                --volume "$PWD:/assets:z" \
                "${KUBE_SCHEDULER_OPERATOR_IMAGE}" \
                /usr/bin/cluster-kube-scheduler-operator render \
                --asset-input-dir=/assets/tls \
                --asset-output-dir=/assets/kube-scheduler-bootstrap \
                --config-override-file=/usr/share/bootkube/manifests/config/config-overrides.yaml \
                --config-output-file=/assets/kube-scheduler-bootstrap/config

        # TODO: copy the bootstrap manifests to replace kube-core-operator
        cp --recursive kube-scheduler-bootstrap/manifests/00_openshift-kube-scheduler-ns.yaml manifests/00_openshift-kube-scheduler-ns.yaml
        cp --recursive kube-scheduler-bootstrap/manifests/secret-* manifests/
        cp --recursive kube-scheduler-bootstrap/manifests/configmap-* manifests/
fi

if [ ! -d mco-bootstrap ]
then
	echo "Rendering MCO manifests..."

	# shellcheck disable=SC2154
	podman run \
		--user 0 \
		--volume "$PWD:/assets:z" \
		"${MACHINE_CONFIG_OPERATOR_IMAGE}" \
		bootstrap \
			--etcd-ca=/assets/tls/etcd-client-ca.crt \
			--root-ca=/assets/tls/root-ca.crt \
			--config-file=/assets/manifests/cluster-config.yaml \
			--dest-dir=/assets/mco-bootstrap \
			--machine-config-controller-image=${MACHINE_CONFIG_CONTROLLER_IMAGE} \
			--machine-config-server-image=${MACHINE_CONFIG_SERVER_IMAGE} \
			--machine-config-daemon-image=${MACHINE_CONFIG_DAEMON_IMAGE} \

	# Bootstrap MachineConfigController uses /etc/mcc/bootstrap/manifests/ dir to
	# 1. read the controller config rendered by MachineConfigOperator
	# 2. read the default MachineConfigPools rendered by MachineConfigOperator
	# 3. read any additional MachineConfigs that are needed for the default MachineConfigPools.
	mkdir --parents /etc/mcc/bootstrap/
	cp --recursive mco-bootstrap/manifests /etc/mcc/bootstrap/manifests
	cp mco-bootstrap/machineconfigoperator-bootstrap-pod.yaml /etc/kubernetes/manifests/

	# /etc/ssl/mcs/tls.{crt, key} are locations for MachineConfigServer's tls assets.
	mkdir --parents /etc/ssl/mcs/
	cp tls/machine-config-server.crt /etc/ssl/mcs/tls.crt
	cp tls/machine-config-server.key /etc/ssl/mcs/tls.key
fi

# We originally wanted to run the etcd cert signer as
# a static pod, but kubelet could't remove static pod
# when API server is not up, so we have to run this as
# podman container.
# See https://github.com/kubernetes/kubernetes/issues/43292

echo "Starting etcd certificate signer..."

trap "podman rm --force etcd-signer" ERR

# shellcheck disable=SC2154
podman run \
	--name etcd-signer \
	--detach \
	--volume /opt/tectonic/tls:/opt/tectonic/tls:ro,z \
	--network host \
	"{{.EtcdCertSignerImage}}" \
	serve \
	--cacrt=/opt/tectonic/tls/etcd-client-ca.crt \
	--cakey=/opt/tectonic/tls/etcd-client-ca.key \
	--servcrt=/opt/tectonic/tls/apiserver.crt \
	--servkey=/opt/tectonic/tls/apiserver.key \
	--address=0.0.0.0:6443 \
	--csrdir=/tmp \
	--peercertdur=26280h \
	--servercertdur=26280h

echo "Waiting for etcd cluster..."

# Wait for the etcd cluster to come up.
set +e
# shellcheck disable=SC2154,SC2086
until podman run \
		--rm \
		--network host \
		--name etcdctl \
		--env ETCDCTL_API=3 \
		--volume /opt/tectonic/tls:/opt/tectonic/tls:ro,z \
		"{{.EtcdctlImage}}" \
		/usr/local/bin/etcdctl \
		--dial-timeout=10m \
		--cacert=/opt/tectonic/tls/etcd-client-ca.crt \
		--cert=/opt/tectonic/tls/etcd-client.crt \
		--key=/opt/tectonic/tls/etcd-client.key \
		--endpoints={{.EtcdCluster}} \
		endpoint health
do
	echo "etcdctl failed. Retrying in 5 seconds..."
	sleep 5
done
set -e

echo "etcd cluster up. Killing etcd certificate signer..."

podman rm --force etcd-signer
rm --force /etc/kubernetes/manifests/machineconfigoperator-bootstrap-pod.yaml

echo "Starting bootkube..."

# shellcheck disable=SC2154
podman run \
	--rm \
	--volume "$PWD:/assets:z" \
	--volume /etc/kubernetes:/etc/kubernetes:z \
	--network=host \
	--entrypoint=/bootkube \
	"{{.BootkubeImage}}" \
	start --asset-dir=/assets`))
)
<commit_msg>bootkube: use etcd-client-ca as serving CA of etcd<commit_after>package content

import (
	"text/template"
)

const (
	// BootkubeSystemdContents is a service for running bootkube on the bootstrap
	// nodes
	BootkubeSystemdContents = `[Unit]
Description=Bootstrap a Kubernetes cluster
Wants=kubelet.service
After=kubelet.service

[Service]
WorkingDirectory=/opt/tectonic

ExecStart=/opt/tectonic/bootkube.sh

Restart=on-failure
RestartSec=5s`
)

var (
	// BootkubeShFileTemplate is a script file for running bootkube on the
	// bootstrap nodes.
	BootkubeShFileTemplate = template.Must(template.New("bootkube.sh").Parse(`#!/usr/bin/env bash
set -e

mkdir --parents /etc/kubernetes/manifests/

MACHINE_CONFIG_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-operator)
MACHINE_CONFIG_CONTROLLER_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-controller)
MACHINE_CONFIG_SERVER_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-server)
MACHINE_CONFIG_DAEMON_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-daemon)

KUBE_APISERVER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-apiserver-operator)
KUBE_CONTROLLER_MANAGER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-controller-manager-operator)
KUBE_SCHEDULER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-scheduler-operator)

if [ ! -d cvo-bootstrap ]
then
	echo "Rendering Cluster Version Operator Manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"{{.ReleaseImage}}" \
		render \
			--output-dir=/assets/cvo-bootstrap \
			--release-image="{{.ReleaseImage}}"

	cp --recursive cvo-bootstrap/manifests .
	cp --recursive cvo-bootstrap/bootstrap/bootstrap-pod.yaml /etc/kubernetes/manifests/
fi

if [ ! -d kco-bootstrap ]
then
	echo "Rendering Kubernetes core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		--volume /etc/kubernetes:/etc/kubernetes:z \
		"{{.KubeCoreRenderImage}}" \
		--config=/assets/kco-config.yaml \
		--output=/assets/kco-bootstrap

	cp --recursive kco-bootstrap/bootstrap-configs /etc/kubernetes/bootstrap-configs
	cp --recursive kco-bootstrap/bootstrap-manifests .
	cp --recursive kco-bootstrap/manifests .
fi

if [ ! -d kube-apiserver-bootstrap ]
then
	echo "Rendering Kubernetes API server core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_APISERVER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-apiserver-operator render \
		--manifest-etcd-serving-ca=etcd-client-ca.crt \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-apiserver-bootstrap \
		--config-override-file=/usr/share/bootkube/manifests/config/config-overrides.yaml \
		--config-output-file=/assets/kube-apiserver-bootstrap/config

	# TODO: copy the bootstrap manifests to replace kube-core-operator
	cp --recursive kube-apiserver-bootstrap/manifests/00_openshift-kube-apiserver-ns.yaml manifests/00_openshift-kube-apiserver-ns.yaml
	cp --recursive kube-apiserver-bootstrap/manifests/secret-* manifests/
	cp --recursive kube-apiserver-bootstrap/manifests/configmap-* manifests/
fi

if [ ! -d kube-controller-manager-bootstrap ]
then
	echo "Rendering Kubernetes Controller Manager core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_CONTROLLER_MANAGER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-controller-manager-operator render \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-controller-manager-bootstrap \
		--config-override-file=/usr/share/bootkube/manifests/config/config-overrides.yaml \
		--config-output-file=/assets/kube-controller-manager-bootstrap/config

	# TODO: copy the bootstrap manifests to replace kube-core-operator
	cp --recursive kube-controller-manager-bootstrap/manifests/00_openshift-kube-controller-manager-ns.yaml manifests/00_openshift-kube-controller-manager-ns.yaml
	cp --recursive kube-controller-manager-bootstrap/manifests/secret-* manifests/
	cp --recursive kube-controller-manager-bootstrap/manifests/configmap-* manifests/
fi

if [ ! -d kube-scheduler-bootstrap ]
then
        echo "Rendering Kubernetes Scheduler core manifests..."

        # shellcheck disable=SC2154
        podman run \
                --volume "$PWD:/assets:z" \
                "${KUBE_SCHEDULER_OPERATOR_IMAGE}" \
                /usr/bin/cluster-kube-scheduler-operator render \
                --asset-input-dir=/assets/tls \
                --asset-output-dir=/assets/kube-scheduler-bootstrap \
                --config-override-file=/usr/share/bootkube/manifests/config/config-overrides.yaml \
                --config-output-file=/assets/kube-scheduler-bootstrap/config

        # TODO: copy the bootstrap manifests to replace kube-core-operator
        cp --recursive kube-scheduler-bootstrap/manifests/00_openshift-kube-scheduler-ns.yaml manifests/00_openshift-kube-scheduler-ns.yaml
        cp --recursive kube-scheduler-bootstrap/manifests/secret-* manifests/
        cp --recursive kube-scheduler-bootstrap/manifests/configmap-* manifests/
fi

if [ ! -d mco-bootstrap ]
then
	echo "Rendering MCO manifests..."

	# shellcheck disable=SC2154
	podman run \
		--user 0 \
		--volume "$PWD:/assets:z" \
		"${MACHINE_CONFIG_OPERATOR_IMAGE}" \
		bootstrap \
			--etcd-ca=/assets/tls/etcd-client-ca.crt \
			--root-ca=/assets/tls/root-ca.crt \
			--config-file=/assets/manifests/cluster-config.yaml \
			--dest-dir=/assets/mco-bootstrap \
			--machine-config-controller-image=${MACHINE_CONFIG_CONTROLLER_IMAGE} \
			--machine-config-server-image=${MACHINE_CONFIG_SERVER_IMAGE} \
			--machine-config-daemon-image=${MACHINE_CONFIG_DAEMON_IMAGE} \

	# Bootstrap MachineConfigController uses /etc/mcc/bootstrap/manifests/ dir to
	# 1. read the controller config rendered by MachineConfigOperator
	# 2. read the default MachineConfigPools rendered by MachineConfigOperator
	# 3. read any additional MachineConfigs that are needed for the default MachineConfigPools.
	mkdir --parents /etc/mcc/bootstrap/
	cp --recursive mco-bootstrap/manifests /etc/mcc/bootstrap/manifests
	cp mco-bootstrap/machineconfigoperator-bootstrap-pod.yaml /etc/kubernetes/manifests/

	# /etc/ssl/mcs/tls.{crt, key} are locations for MachineConfigServer's tls assets.
	mkdir --parents /etc/ssl/mcs/
	cp tls/machine-config-server.crt /etc/ssl/mcs/tls.crt
	cp tls/machine-config-server.key /etc/ssl/mcs/tls.key
fi

# We originally wanted to run the etcd cert signer as
# a static pod, but kubelet could't remove static pod
# when API server is not up, so we have to run this as
# podman container.
# See https://github.com/kubernetes/kubernetes/issues/43292

echo "Starting etcd certificate signer..."

trap "podman rm --force etcd-signer" ERR

# shellcheck disable=SC2154
podman run \
	--name etcd-signer \
	--detach \
	--volume /opt/tectonic/tls:/opt/tectonic/tls:ro,z \
	--network host \
	"{{.EtcdCertSignerImage}}" \
	serve \
	--cacrt=/opt/tectonic/tls/etcd-client-ca.crt \
	--cakey=/opt/tectonic/tls/etcd-client-ca.key \
	--servcrt=/opt/tectonic/tls/apiserver.crt \
	--servkey=/opt/tectonic/tls/apiserver.key \
	--address=0.0.0.0:6443 \
	--csrdir=/tmp \
	--peercertdur=26280h \
	--servercertdur=26280h

echo "Waiting for etcd cluster..."

# Wait for the etcd cluster to come up.
set +e
# shellcheck disable=SC2154,SC2086
until podman run \
		--rm \
		--network host \
		--name etcdctl \
		--env ETCDCTL_API=3 \
		--volume /opt/tectonic/tls:/opt/tectonic/tls:ro,z \
		"{{.EtcdctlImage}}" \
		/usr/local/bin/etcdctl \
		--dial-timeout=10m \
		--cacert=/opt/tectonic/tls/etcd-client-ca.crt \
		--cert=/opt/tectonic/tls/etcd-client.crt \
		--key=/opt/tectonic/tls/etcd-client.key \
		--endpoints={{.EtcdCluster}} \
		endpoint health
do
	echo "etcdctl failed. Retrying in 5 seconds..."
	sleep 5
done
set -e

echo "etcd cluster up. Killing etcd certificate signer..."

podman rm --force etcd-signer
rm --force /etc/kubernetes/manifests/machineconfigoperator-bootstrap-pod.yaml

echo "Starting bootkube..."

# shellcheck disable=SC2154
podman run \
	--rm \
	--volume "$PWD:/assets:z" \
	--volume /etc/kubernetes:/etc/kubernetes:z \
	--network=host \
	--entrypoint=/bootkube \
	"{{.BootkubeImage}}" \
	start --asset-dir=/assets`))
)
<|endoftext|>


<commit_before>/*
Copyright 2017 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 create

import (
	"context"
	"fmt"

	"github.com/spf13/cobra"

	corev1 "k8s.io/api/core/v1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/resource"
	schedulingv1client "k8s.io/client-go/kubernetes/typed/scheduling/v1"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/scheme"
	"k8s.io/kubectl/pkg/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
)

var (
	pcLong = templates.LongDesc(i18n.T(`
		Create a priorityclass with the specified name, value, globalDefault and description`))

	pcExample = templates.Examples(i18n.T(`
		# Create a priorityclass named high-priority
		kubectl create priorityclass high-priority --value=1000 --description="high priority"

		# Create a priorityclass named default-priority that considered as the global default priority
		kubectl create priorityclass default-priority --value=1000 --global-default=true --description="default priority"

		# Create a priorityclass named high-priority that can not preempt pods with lower priority
		kubectl create priorityclass high-priority --value=1000 --description="high priority" --preemption-policy="Never"`))
)

// PriorityClassOptions holds the options for 'create priorityclass' sub command
type PriorityClassOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	PrintObj   func(obj runtime.Object) error

	Name             string
	Value            int32
	GlobalDefault    bool
	Description      string
	PreemptionPolicy string
	FieldManager     string
	CreateAnnotation bool

	Client         *schedulingv1client.SchedulingV1Client
	DryRunStrategy cmdutil.DryRunStrategy
	DryRunVerifier *resource.DryRunVerifier

	genericclioptions.IOStreams
}

// NewPriorityClassOptions returns an initialized PriorityClassOptions instance
func NewPriorityClassOptions(ioStreams genericclioptions.IOStreams) *PriorityClassOptions {
	return &PriorityClassOptions{
		Value:            0,
		PreemptionPolicy: "PreemptLowerPriority",
		PrintFlags:       genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
		IOStreams:        ioStreams,
	}
}

// NewCmdCreatePriorityClass is a macro command to create a new priorityClass.
func NewCmdCreatePriorityClass(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewPriorityClassOptions(ioStreams)

	cmd := &cobra.Command{
		Use:                   "priorityclass NAME --value=VALUE --global-default=BOOL [--dry-run=server|client|none]",
		DisableFlagsInUseLine: true,
		Aliases:               []string{"pc"},
		Short:                 i18n.T("Create a priorityclass with the specified name."),
		Long:                  pcLong,
		Example:               pcExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd, args))
			cmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)

	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddDryRunFlag(cmd)
	cmd.Flags().Int32Var(&o.Value, "value", o.Value, i18n.T("the value of this priority class."))
	cmd.Flags().BoolVar(&o.GlobalDefault, "global-default", o.GlobalDefault, i18n.T("global-default specifies whether this PriorityClass should be considered as the default priority."))
	cmd.Flags().StringVar(&o.Description, "description", o.Description, i18n.T("description is an arbitrary string that usually provides guidelines on when this priority class should be used."))
	cmd.Flags().StringVar(&o.PreemptionPolicy, "preemption-policy", o.PreemptionPolicy, i18n.T("preemption-policy is the policy for preempting pods with lower priority."))
	cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
	return cmd
}

// Complete completes all the required options
func (o *PriorityClassOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	var err error
	o.Name, err = NameFromCommandArgs(cmd, args)
	if err != nil {
		return err
	}

	restConfig, err := f.ToRESTConfig()
	if err != nil {
		return err
	}
	o.Client, err = schedulingv1client.NewForConfig(restConfig)
	if err != nil {
		return err
	}

	o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)

	o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
	if err != nil {
		return err
	}
	dynamicClient, err := f.DynamicClient()
	if err != nil {
		return err
	}
	discoveryClient, err := f.ToDiscoveryClient()
	if err != nil {
		return err
	}
	o.DryRunVerifier = resource.NewDryRunVerifier(dynamicClient, discoveryClient)
	cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)

	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}
	o.PrintObj = func(obj runtime.Object) error {
		return printer.PrintObj(obj, o.Out)
	}

	return nil
}

// Run calls the CreateSubcommandOptions.Run in the PriorityClassOptions instance
func (o *PriorityClassOptions) Run() error {
	priorityClass, err := o.createPriorityClass()
	if err != nil {
		return err
	}

	if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, priorityClass, scheme.DefaultJSONEncoder()); err != nil {
		return err
	}

	if o.DryRunStrategy != cmdutil.DryRunClient {
		createOptions := metav1.CreateOptions{}
		if o.FieldManager != "" {
			createOptions.FieldManager = o.FieldManager
		}
		if o.DryRunStrategy == cmdutil.DryRunServer {
			if err := o.DryRunVerifier.HasSupport(priorityClass.GroupVersionKind()); err != nil {
				return err
			}
			createOptions.DryRun = []string{metav1.DryRunAll}
		}
		var err error
		priorityClass, err = o.Client.PriorityClasses().Create(context.TODO(), priorityClass, createOptions)
		if err != nil {
			return fmt.Errorf("failed to create clusterrolebinding: %v", err)
		}
	}

	return o.PrintObj(priorityClass)
}

func (o *PriorityClassOptions) createPriorityClass() (*schedulingv1.PriorityClass, error) {
	preemptionPolicy := corev1.PreemptionPolicy(o.PreemptionPolicy)
	return &schedulingv1.PriorityClass{
		// this is ok because we know exactly how we want to be serialized
		TypeMeta: metav1.TypeMeta{APIVersion: schedulingv1.SchemeGroupVersion.String(), Kind: "PriorityClass"},
		ObjectMeta: metav1.ObjectMeta{
			Name: o.Name,
		},
		Value:            o.Value,
		GlobalDefault:    o.GlobalDefault,
		Description:      o.Description,
		PreemptionPolicy: &preemptionPolicy,
	}, nil
}
<commit_msg>Fixed typo in create_priorityclass<commit_after>/*
Copyright 2017 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 create

import (
	"context"
	"fmt"

	"github.com/spf13/cobra"

	corev1 "k8s.io/api/core/v1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/resource"
	schedulingv1client "k8s.io/client-go/kubernetes/typed/scheduling/v1"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/scheme"
	"k8s.io/kubectl/pkg/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
)

var (
	pcLong = templates.LongDesc(i18n.T(`
		Create a priorityclass with the specified name, value, globalDefault and description`))

	pcExample = templates.Examples(i18n.T(`
		# Create a priorityclass named high-priority
		kubectl create priorityclass high-priority --value=1000 --description="high priority"

		# Create a priorityclass named default-priority that considered as the global default priority
		kubectl create priorityclass default-priority --value=1000 --global-default=true --description="default priority"

		# Create a priorityclass named high-priority that can not preempt pods with lower priority
		kubectl create priorityclass high-priority --value=1000 --description="high priority" --preemption-policy="Never"`))
)

// PriorityClassOptions holds the options for 'create priorityclass' sub command
type PriorityClassOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	PrintObj   func(obj runtime.Object) error

	Name             string
	Value            int32
	GlobalDefault    bool
	Description      string
	PreemptionPolicy string
	FieldManager     string
	CreateAnnotation bool

	Client         *schedulingv1client.SchedulingV1Client
	DryRunStrategy cmdutil.DryRunStrategy
	DryRunVerifier *resource.DryRunVerifier

	genericclioptions.IOStreams
}

// NewPriorityClassOptions returns an initialized PriorityClassOptions instance
func NewPriorityClassOptions(ioStreams genericclioptions.IOStreams) *PriorityClassOptions {
	return &PriorityClassOptions{
		Value:            0,
		PreemptionPolicy: "PreemptLowerPriority",
		PrintFlags:       genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
		IOStreams:        ioStreams,
	}
}

// NewCmdCreatePriorityClass is a macro command to create a new priorityClass.
func NewCmdCreatePriorityClass(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewPriorityClassOptions(ioStreams)

	cmd := &cobra.Command{
		Use:                   "priorityclass NAME --value=VALUE --global-default=BOOL [--dry-run=server|client|none]",
		DisableFlagsInUseLine: true,
		Aliases:               []string{"pc"},
		Short:                 i18n.T("Create a priorityclass with the specified name."),
		Long:                  pcLong,
		Example:               pcExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd, args))
			cmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)

	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddDryRunFlag(cmd)
	cmd.Flags().Int32Var(&o.Value, "value", o.Value, i18n.T("the value of this priority class."))
	cmd.Flags().BoolVar(&o.GlobalDefault, "global-default", o.GlobalDefault, i18n.T("global-default specifies whether this PriorityClass should be considered as the default priority."))
	cmd.Flags().StringVar(&o.Description, "description", o.Description, i18n.T("description is an arbitrary string that usually provides guidelines on when this priority class should be used."))
	cmd.Flags().StringVar(&o.PreemptionPolicy, "preemption-policy", o.PreemptionPolicy, i18n.T("preemption-policy is the policy for preempting pods with lower priority."))
	cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
	return cmd
}

// Complete completes all the required options
func (o *PriorityClassOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	var err error
	o.Name, err = NameFromCommandArgs(cmd, args)
	if err != nil {
		return err
	}

	restConfig, err := f.ToRESTConfig()
	if err != nil {
		return err
	}
	o.Client, err = schedulingv1client.NewForConfig(restConfig)
	if err != nil {
		return err
	}

	o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)

	o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
	if err != nil {
		return err
	}
	dynamicClient, err := f.DynamicClient()
	if err != nil {
		return err
	}
	discoveryClient, err := f.ToDiscoveryClient()
	if err != nil {
		return err
	}
	o.DryRunVerifier = resource.NewDryRunVerifier(dynamicClient, discoveryClient)
	cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)

	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}
	o.PrintObj = func(obj runtime.Object) error {
		return printer.PrintObj(obj, o.Out)
	}

	return nil
}

// Run calls the CreateSubcommandOptions.Run in the PriorityClassOptions instance
func (o *PriorityClassOptions) Run() error {
	priorityClass, err := o.createPriorityClass()
	if err != nil {
		return err
	}

	if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, priorityClass, scheme.DefaultJSONEncoder()); err != nil {
		return err
	}

	if o.DryRunStrategy != cmdutil.DryRunClient {
		createOptions := metav1.CreateOptions{}
		if o.FieldManager != "" {
			createOptions.FieldManager = o.FieldManager
		}
		if o.DryRunStrategy == cmdutil.DryRunServer {
			if err := o.DryRunVerifier.HasSupport(priorityClass.GroupVersionKind()); err != nil {
				return err
			}
			createOptions.DryRun = []string{metav1.DryRunAll}
		}
		var err error
		priorityClass, err = o.Client.PriorityClasses().Create(context.TODO(), priorityClass, createOptions)
		if err != nil {
			return fmt.Errorf("failed to create priorityclass: %v", err)
		}
	}

	return o.PrintObj(priorityClass)
}

func (o *PriorityClassOptions) createPriorityClass() (*schedulingv1.PriorityClass, error) {
	preemptionPolicy := corev1.PreemptionPolicy(o.PreemptionPolicy)
	return &schedulingv1.PriorityClass{
		// this is ok because we know exactly how we want to be serialized
		TypeMeta: metav1.TypeMeta{APIVersion: schedulingv1.SchemeGroupVersion.String(), Kind: "PriorityClass"},
		ObjectMeta: metav1.ObjectMeta{
			Name: o.Name,
		},
		Value:            o.Value,
		GlobalDefault:    o.GlobalDefault,
		Description:      o.Description,
		PreemptionPolicy: &preemptionPolicy,
	}, nil
}
<|endoftext|>


<commit_before>// Copyright 2019 the Service Broker Project 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 builtin

import (
	"context"
	"testing"

	"github.com/GoogleCloudPlatform/gcp-service-broker/db_service/models"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/broker"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/base"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/base/basefakes"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/bigquery"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/bigtable"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/cloudsql"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/dataflow"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/datastore"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/dialogflow"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/firestore"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/ml"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/pubsub"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/spanner"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/stackdriver"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/storage"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/varcontext"
)

func TestServiceProviderAsync(t *testing.T) {
	cases := map[string]struct {
		AsyncProvisionExpected   bool
		AsyncDeprovisionExpected bool
		Provider                 broker.ServiceProvider
	}{
		"ml": {
			Provider: &ml.ApiServiceBroker{},
		},
		"bigquery": {
			Provider: &bigquery.BigQueryBroker{},
		},
		"bigtable": {
			Provider: &bigtable.BigTableBroker{},
		},
		"cloudsql": {
			AsyncProvisionExpected:   true,
			AsyncDeprovisionExpected: true,
			Provider:                 &cloudsql.CloudSQLBroker{},
		},
		"dataflow": {
			Provider: &dataflow.DataflowBroker{},
		},
		"datastore": {
			Provider: &datastore.DatastoreBroker{},
		},
		"dialogflow": {
			Provider: &dialogflow.DialogflowBroker{},
		},
		"firestore": {
			Provider: &firestore.FirestoreBroker{},
		},
		"pubsub": {
			Provider: &pubsub.PubSubBroker{},
		},
		"spanner": {
			AsyncProvisionExpected: true,
			Provider:               &spanner.SpannerBroker{},
		},
		"storage": {
			Provider: &storage.StorageBroker{},
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			actualProvisionAsync := tc.Provider.ProvisionsAsync()
			if tc.AsyncProvisionExpected != actualProvisionAsync {
				t.Errorf("Expected async provision to match. Expected: %t, Actual: %t", tc.AsyncProvisionExpected, actualProvisionAsync)
			}

			actualDeprovisionAsync := tc.Provider.DeprovisionsAsync()
			if tc.AsyncDeprovisionExpected != actualDeprovisionAsync {
				t.Errorf("Expected async deprovision to match. Expected: %t, Actual: %t", tc.AsyncDeprovisionExpected, actualDeprovisionAsync)
			}
		})
	}
}

func TestThinWrapperServiceProviders(t *testing.T) {
	cases := map[string]func(base.BrokerBase) broker.ServiceProvider{
		"pubsub": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &pubsub.PubSubBroker{BrokerBase: brokerBase}
		},
		"stackdriver": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &stackdriver.StackdriverAccountProvider{BrokerBase: brokerBase}
		},
		"ml": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &ml.ApiServiceBroker{BrokerBase: brokerBase}
		},
		"bigquery": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &bigquery.BigQueryBroker{BrokerBase: brokerBase}
		},
		"dataflow": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &dataflow.DataflowBroker{BrokerBase: brokerBase}
		},
		"datastore": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &datastore.DatastoreBroker{BrokerBase: brokerBase}
		},
		"dialogflow": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &dialogflow.DialogflowBroker{BrokerBase: brokerBase}
		},
		"firestore": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &firestore.FirestoreBroker{BrokerBase: brokerBase}
		},
		"spanner": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &spanner.SpannerBroker{BrokerBase: brokerBase}
		},
		"storage": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &storage.StorageBroker{BrokerBase: brokerBase}
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			accountManager := basefakes.FakeServiceAccountManager{}
			brokerBase := base.BrokerBase{
				AccountManager: &accountManager,
			}
			serviceProvider := tc(brokerBase)

			if _, err := serviceProvider.Bind(context.Background(), &varcontext.VarContext{}); err != nil {
				t.Fatal(err)
			}
			if accountManager.CreateCredentialsCallCount() != 1 {
				t.Errorf("Expected CreateCredentials to be called once. Expected: %d, Actual: %d", 1, accountManager.CreateCredentialsCallCount())
			}

			if err := serviceProvider.Unbind(context.Background(), models.ServiceInstanceDetails{}, models.ServiceBindingCredentials{}); err != nil {
				t.Fatal(err)
			}
			if accountManager.DeleteCredentialsCallCount() != 1 {
				t.Errorf("Expected DeleteCredentials to be called once. Expected: %d, Actual: %d", 1, accountManager.DeleteCredentialsCallCount())
			}
		})
	}
}
<commit_msg>fixed botched test<commit_after>// Copyright 2019 the Service Broker Project 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 builtin

import (
	"context"
	"testing"

	"github.com/GoogleCloudPlatform/gcp-service-broker/db_service/models"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/broker"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/base"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/base/basefakes"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/bigquery"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/bigtable"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/cloudsql"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/dataflow"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/datastore"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/dialogflow"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/firestore"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/ml"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/pubsub"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/spanner"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/stackdriver"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/storage"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/varcontext"
)

func TestServiceProviderAsync(t *testing.T) {
	cases := map[string]struct {
		AsyncProvisionExpected   bool
		AsyncDeprovisionExpected bool
		Provider                 broker.ServiceProvider
	}{
		"ml": {
			Provider: &ml.ApiServiceBroker{},
		},
		"bigquery": {
			Provider: &bigquery.BigQueryBroker{},
		},
		"bigtable": {
			Provider: &bigtable.BigTableBroker{},
		},
		"cloudsql": {
			AsyncProvisionExpected:   true,
			AsyncDeprovisionExpected: false,
			Provider:                 &cloudsql.CloudSQLBroker{},
		},
		"dataflow": {
			Provider: &dataflow.DataflowBroker{},
		},
		"datastore": {
			Provider: &datastore.DatastoreBroker{},
		},
		"dialogflow": {
			Provider: &dialogflow.DialogflowBroker{},
		},
		"firestore": {
			Provider: &firestore.FirestoreBroker{},
		},
		"pubsub": {
			Provider: &pubsub.PubSubBroker{},
		},
		"spanner": {
			AsyncProvisionExpected: true,
			Provider:               &spanner.SpannerBroker{},
		},
		"storage": {
			Provider: &storage.StorageBroker{},
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			actualProvisionAsync := tc.Provider.ProvisionsAsync()
			if tc.AsyncProvisionExpected != actualProvisionAsync {
				t.Errorf("Expected async provision to match. Expected: %t, Actual: %t", tc.AsyncProvisionExpected, actualProvisionAsync)
			}

			actualDeprovisionAsync := tc.Provider.DeprovisionsAsync()
			if tc.AsyncDeprovisionExpected != actualDeprovisionAsync {
				t.Errorf("Expected async deprovision to match. Expected: %t, Actual: %t", tc.AsyncDeprovisionExpected, actualDeprovisionAsync)
			}
		})
	}
}

func TestThinWrapperServiceProviders(t *testing.T) {
	cases := map[string]func(base.BrokerBase) broker.ServiceProvider{
		"pubsub": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &pubsub.PubSubBroker{BrokerBase: brokerBase}
		},
		"stackdriver": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &stackdriver.StackdriverAccountProvider{BrokerBase: brokerBase}
		},
		"ml": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &ml.ApiServiceBroker{BrokerBase: brokerBase}
		},
		"bigquery": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &bigquery.BigQueryBroker{BrokerBase: brokerBase}
		},
		"dataflow": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &dataflow.DataflowBroker{BrokerBase: brokerBase}
		},
		"datastore": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &datastore.DatastoreBroker{BrokerBase: brokerBase}
		},
		"dialogflow": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &dialogflow.DialogflowBroker{BrokerBase: brokerBase}
		},
		"firestore": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &firestore.FirestoreBroker{BrokerBase: brokerBase}
		},
		"spanner": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &spanner.SpannerBroker{BrokerBase: brokerBase}
		},
		"storage": func(brokerBase base.BrokerBase) broker.ServiceProvider {
			return &storage.StorageBroker{BrokerBase: brokerBase}
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			accountManager := basefakes.FakeServiceAccountManager{}
			brokerBase := base.BrokerBase{
				AccountManager: &accountManager,
			}
			serviceProvider := tc(brokerBase)

			if _, err := serviceProvider.Bind(context.Background(), &varcontext.VarContext{}); err != nil {
				t.Fatal(err)
			}
			if accountManager.CreateCredentialsCallCount() != 1 {
				t.Errorf("Expected CreateCredentials to be called once. Expected: %d, Actual: %d", 1, accountManager.CreateCredentialsCallCount())
			}

			if err := serviceProvider.Unbind(context.Background(), models.ServiceInstanceDetails{}, models.ServiceBindingCredentials{}); err != nil {
				t.Fatal(err)
			}
			if accountManager.DeleteCredentialsCallCount() != 1 {
				t.Errorf("Expected DeleteCredentials to be called once. Expected: %d, Actual: %d", 1, accountManager.DeleteCredentialsCallCount())
			}
		})
	}
}
<|endoftext|>


<commit_before>package oci

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/containers/image/manifest"
	"github.com/containers/image/types"
)

type ociManifest struct {
	SchemaVersion int               `json:"schemaVersion"`
	MediaType     string            `json:"mediaType"`
	Config        descriptor        `json:"config"`
	Layers        []descriptor      `json:"layers"`
	Annotations   map[string]string `json:"annotations"`
}

type descriptor struct {
	Digest    string `json:"digest"`
	MediaType string `json:"mediaType"`
	Size      int64  `json:"size"`
}

type ociImageDestination struct {
	dir string
	tag string
}

var refRegexp = regexp.MustCompile(`^([A-Za-z0-9._-]+)+$`)

// NewOCIImageDestination returns an ImageDestination for writing to an existing directory.
func NewOCIImageDestination(dest string) (types.ImageDestination, error) {
	dir := dest
	sep := strings.LastIndex(dest, ":")
	tag := "latest"
	if sep != -1 {
		dir = dest[:sep]
		tag = dest[sep+1:]
		if !refRegexp.MatchString(tag) {
			return nil, fmt.Errorf("Invalid reference %s", tag)
		}
	}
	return &ociImageDestination{
		dir: dir,
		tag: tag,
	}, nil
}

func (d *ociImageDestination) CanonicalDockerReference() (string, error) {
	return "", fmt.Errorf("Can not determine canonical Docker reference for an OCI image")
}

func createManifest(m []byte) ([]byte, string, error) {
	om := ociManifest{}
	mt := manifest.GuessMIMEType(m)
	switch mt {
	case manifest.DockerV2Schema1MIMEType:
		// There a simple reason about not yet implementing this.
		// OCI image-spec assure about backward compatibility with docker v2s2 but not v2s1
		// generating a v2s2 is a migration docker does when upgrading to 1.10.3
		// and I don't think we should bother about this now (I don't want to have migration code here in skopeo)
		return nil, "", fmt.Errorf("can't create OCI manifest from Docker V2 schema 1 manifest")
	case manifest.DockerV2Schema2MIMEType:
		if err := json.Unmarshal(m, &om); err != nil {
			return nil, "", err
		}
		om.MediaType = manifest.OCIV1ImageManifestMIMEType
		for i := range om.Layers {
			om.Layers[i].MediaType = manifest.OCIV1ImageSerializationMIMEType
		}
		om.Config.MediaType = manifest.OCIV1ImageSerializationConfigMIMEType
		b, err := json.Marshal(om)
		if err != nil {
			return nil, "", err
		}
		return b, om.MediaType, nil
	case manifest.DockerV2ListMIMEType:
		return nil, "", fmt.Errorf("can't create OCI manifest from Docker V2 schema 2 manifest list")
	case manifest.OCIV1ImageManifestListMIMEType:
		return nil, "", fmt.Errorf("can't create OCI manifest from OCI manifest list")
	case manifest.OCIV1ImageManifestMIMEType:
		return m, om.MediaType, nil
	}
	return nil, "", fmt.Errorf("Unrecognized manifest media type")
}

func (d *ociImageDestination) PutManifest(m []byte) error {
	if err := d.ensureParentDirectoryExists("refs"); err != nil {
		return err
	}
	// TODO(mitr, runcom): this breaks signatures entirely since at this point we're creating a new manifest
	// and signatures don't apply anymore. Will fix.
	ociMan, mt, err := createManifest(m)
	if err != nil {
		return err
	}
	digest, err := manifest.Digest(ociMan)
	if err != nil {
		return err
	}
	desc := descriptor{}
	desc.Digest = digest
	// TODO(runcom): beaware and add support for OCI manifest list
	desc.MediaType = mt
	desc.Size = int64(len(ociMan))
	data, err := json.Marshal(desc)
	if err != nil {
		return err
	}

	if err := ioutil.WriteFile(blobPath(d.dir, digest), ociMan, 0644); err != nil {
		return err
	}
	// TODO(runcom): ugly here?
	if err := ioutil.WriteFile(ociLayoutPath(d.dir), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0644); err != nil {
		return err
	}
	return ioutil.WriteFile(descriptorPath(d.dir, d.tag), data, 0644)
}

func (d *ociImageDestination) PutBlob(digest string, stream io.Reader) error {
	if err := d.ensureParentDirectoryExists("blobs"); err != nil {
		return err
	}
	blob, err := os.Create(blobPath(d.dir, digest))
	if err != nil {
		return err
	}
	defer blob.Close()
	if _, err := io.Copy(blob, stream); err != nil {
		return err
	}
	if err := blob.Sync(); err != nil {
		return err
	}
	return nil
}

func (d *ociImageDestination) ensureParentDirectoryExists(parent string) error {
	path := filepath.Join(d.dir, parent)
	if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
		if err := os.MkdirAll(path, 0755); err != nil {
			return err
		}
	}
	return nil
}

func (d *ociImageDestination) SupportedManifestMIMETypes() []string {
	return []string{
		manifest.OCIV1ImageManifestMIMEType,
		manifest.DockerV2Schema2MIMEType,
	}
}

func (d *ociImageDestination) PutSignatures(signatures [][]byte) error {
	if len(signatures) != 0 {
		return fmt.Errorf("Pushing signatures for OCI images is not supported")
	}
	return nil
}

// ociLayoutPathPath returns a path for the oci-layout within a directory using OCI conventions.
func ociLayoutPath(dir string) string {
	return filepath.Join(dir, "oci-layout")
}

// blobPath returns a path for a blob within a directory using OCI image-layout conventions.
func blobPath(dir string, digest string) string {
	return filepath.Join(dir, "blobs", strings.Replace(digest, ":", "-", -1))
}

// descriptorPath returns a path for the manifest within a directory using OCI conventions.
func descriptorPath(dir string, digest string) string {
	return filepath.Join(dir, "refs", digest)
}
<commit_msg>oci: cleanup API<commit_after>package oci

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/containers/image/manifest"
	"github.com/containers/image/types"
)

type ociManifest struct {
	SchemaVersion int               `json:"schemaVersion"`
	MediaType     string            `json:"mediaType"`
	Config        descriptor        `json:"config"`
	Layers        []descriptor      `json:"layers"`
	Annotations   map[string]string `json:"annotations"`
}

type descriptor struct {
	Digest    string `json:"digest"`
	MediaType string `json:"mediaType"`
	Size      int64  `json:"size"`
}

type ociImageDestination struct {
	dir string
	tag string
}

var refRegexp = regexp.MustCompile(`^([A-Za-z0-9._-]+)+$`)

// NewImageDestination returns an ImageDestination for writing to an existing directory.
func NewImageDestination(dest string) (types.ImageDestination, error) {
	dir := dest
	sep := strings.LastIndex(dest, ":")
	tag := "latest"
	if sep != -1 {
		dir = dest[:sep]
		tag = dest[sep+1:]
		if !refRegexp.MatchString(tag) {
			return nil, fmt.Errorf("Invalid reference %s", tag)
		}
	}
	return &ociImageDestination{
		dir: dir,
		tag: tag,
	}, nil
}

func (d *ociImageDestination) CanonicalDockerReference() (string, error) {
	return "", fmt.Errorf("Can not determine canonical Docker reference for an OCI image")
}

func createManifest(m []byte) ([]byte, string, error) {
	om := ociManifest{}
	mt := manifest.GuessMIMEType(m)
	switch mt {
	case manifest.DockerV2Schema1MIMEType:
		// There a simple reason about not yet implementing this.
		// OCI image-spec assure about backward compatibility with docker v2s2 but not v2s1
		// generating a v2s2 is a migration docker does when upgrading to 1.10.3
		// and I don't think we should bother about this now (I don't want to have migration code here in skopeo)
		return nil, "", fmt.Errorf("can't create OCI manifest from Docker V2 schema 1 manifest")
	case manifest.DockerV2Schema2MIMEType:
		if err := json.Unmarshal(m, &om); err != nil {
			return nil, "", err
		}
		om.MediaType = manifest.OCIV1ImageManifestMIMEType
		for i := range om.Layers {
			om.Layers[i].MediaType = manifest.OCIV1ImageSerializationMIMEType
		}
		om.Config.MediaType = manifest.OCIV1ImageSerializationConfigMIMEType
		b, err := json.Marshal(om)
		if err != nil {
			return nil, "", err
		}
		return b, om.MediaType, nil
	case manifest.DockerV2ListMIMEType:
		return nil, "", fmt.Errorf("can't create OCI manifest from Docker V2 schema 2 manifest list")
	case manifest.OCIV1ImageManifestListMIMEType:
		return nil, "", fmt.Errorf("can't create OCI manifest from OCI manifest list")
	case manifest.OCIV1ImageManifestMIMEType:
		return m, om.MediaType, nil
	}
	return nil, "", fmt.Errorf("Unrecognized manifest media type")
}

func (d *ociImageDestination) PutManifest(m []byte) error {
	if err := d.ensureParentDirectoryExists("refs"); err != nil {
		return err
	}
	// TODO(mitr, runcom): this breaks signatures entirely since at this point we're creating a new manifest
	// and signatures don't apply anymore. Will fix.
	ociMan, mt, err := createManifest(m)
	if err != nil {
		return err
	}
	digest, err := manifest.Digest(ociMan)
	if err != nil {
		return err
	}
	desc := descriptor{}
	desc.Digest = digest
	// TODO(runcom): beaware and add support for OCI manifest list
	desc.MediaType = mt
	desc.Size = int64(len(ociMan))
	data, err := json.Marshal(desc)
	if err != nil {
		return err
	}

	if err := ioutil.WriteFile(blobPath(d.dir, digest), ociMan, 0644); err != nil {
		return err
	}
	// TODO(runcom): ugly here?
	if err := ioutil.WriteFile(ociLayoutPath(d.dir), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0644); err != nil {
		return err
	}
	return ioutil.WriteFile(descriptorPath(d.dir, d.tag), data, 0644)
}

func (d *ociImageDestination) PutBlob(digest string, stream io.Reader) error {
	if err := d.ensureParentDirectoryExists("blobs"); err != nil {
		return err
	}
	blob, err := os.Create(blobPath(d.dir, digest))
	if err != nil {
		return err
	}
	defer blob.Close()
	if _, err := io.Copy(blob, stream); err != nil {
		return err
	}
	if err := blob.Sync(); err != nil {
		return err
	}
	return nil
}

func (d *ociImageDestination) ensureParentDirectoryExists(parent string) error {
	path := filepath.Join(d.dir, parent)
	if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
		if err := os.MkdirAll(path, 0755); err != nil {
			return err
		}
	}
	return nil
}

func (d *ociImageDestination) SupportedManifestMIMETypes() []string {
	return []string{
		manifest.OCIV1ImageManifestMIMEType,
		manifest.DockerV2Schema2MIMEType,
	}
}

func (d *ociImageDestination) PutSignatures(signatures [][]byte) error {
	if len(signatures) != 0 {
		return fmt.Errorf("Pushing signatures for OCI images is not supported")
	}
	return nil
}

// ociLayoutPathPath returns a path for the oci-layout within a directory using OCI conventions.
func ociLayoutPath(dir string) string {
	return filepath.Join(dir, "oci-layout")
}

// blobPath returns a path for a blob within a directory using OCI image-layout conventions.
func blobPath(dir string, digest string) string {
	return filepath.Join(dir, "blobs", strings.Replace(digest, ":", "-", -1))
}

// descriptorPath returns a path for the manifest within a directory using OCI conventions.
func descriptorPath(dir string, digest string) string {
	return filepath.Join(dir, "refs", digest)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

var input = flag.String("input", "sites.json", "json file to read")
var parsed = flag.String("parsed", "../templates/js/sites_parsed.js", "GIGO.sites_parsed= js file to write for falling-sky")
var raw = flag.String("raw", "../templates/js/sites_parsed_raw.js", "js file to write for other automation")
var validator = flag.String("validator", "http://validator.test-ipv6.com/urlvalidate.cgi", "use this service to validate a url, instead of fetching directly")
var slow = flag.Duration("slow", time.Second*12, "time to consider a lookup 'slow'") // TODO Make this slow check per-url?
var minimumCount = flag.Int("minimum", 0, "Minimum number of sites that must answer, else fail")

// SiteRecord describes a single mirror or "Other Sites" record
type SiteRecord struct {
	Site        string `json:"site"`        // What site name to publicly attribute
	Mirror      bool   `json:"mirror"`      // if true, site is a full mirror; if false, just "OtherSites"
	Hide        bool   `json:"hide"`        // If true, stop sending traffic here
	V4          string `json:"v4"`          // IPv4 test URL  (http or https)
	V6          string `json:"v6"`          // IPv6 test URL (http or https)
	Loc         string `json:"loc"`         // Country where the site is located
	Provider    string `json:"provider"`    // What provider to publicly attribute
	Monitor     string `json:"monitor"`     // Who to notify on error (automation)
	Contact     string `json:"contact"`     // Who to notify on error (human)
	Reason      string `json:"reason"`      // Reason disabled
	Transparent bool   `json:"transparent"` // Whether or not the site is a transparent mirror
}

// SitesMap is a series of site records, keyed by site name
type SitesMap map[string]*SiteRecord

// SitesFile is a container, containing a SitesMap
// This works around the problems with Go unmarshalling to a top level map
type SitesFile struct {
	Sites SitesMap `json:"sites"`
}

// ReadSitesFile reads from disk
func ReadSitesFile(fn string) (*SitesFile, error) {
	var sf SitesFile

	b, err := ioutil.ReadFile(fn)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(b, &sf)
	if err != nil {
		return nil, err
	}

	return &sf, nil
}

func (sm *SitesMap) WriteJS(fn string, prefix string, suffix string) error {
	if fn == "" {
		return nil
	}
	s := prefix + sm.String() + suffix
	b := []byte(s)
	err := ioutil.WriteFile(fn+".new", b, 0755)
	if err != nil {
		return err
	}
	err = os.Rename(fn+".new", fn)
	return err
}

// Bytes returns formatted data
func (sf *SitesFile) Bytes() ([]byte, error) {
	b, err := json.MarshalIndent(sf, "", " ")
	if err != nil {
		log.Fatal(err)
	}
	return b, err
}

// String returns formatted data
func (sf *SitesFile) String() string {
	b, err := sf.Bytes()
	if err != nil {
		log.Fatal(err)
	}
	return string(b)
}

// Bytes returns formatted data
func (sm *SitesMap) Bytes() ([]byte, error) {
	b, err := json.MarshalIndent(sm, "", " ")
	if err != nil {
		log.Fatal(err)
	}
	return b, err
}

// String returns formatted data
func (sm *SitesMap) String() string {
	b, err := sm.Bytes()
	if err != nil {
		log.Fatal(err)
	}
	return string(b)
}

// Print the sites file to stdout
func (sf *SitesFile) Print() {
	fmt.Println(sf.String())
}

// FixDefaults Scans the records and fixes any missing info,
// canonicalizes 1/0 values.
func (sf *SitesFile) FixDefaults() error {
	for key, sr := range sf.Sites {

		// Make sure the "site" is filled out (same as the key)
		if sr.Site == "" {
			sr.Site = key
		}
		// Canonicalize true/false/empty/etc as "1" and "0" strings
	}

	return nil
}

func (sf *SitesFile) DeleteHidden() error {
	for key, sr := range sf.Sites {
		if sr.Hide {
			delete(sf.Sites, key)
		}
	}
	return nil
}
func (sf *SitesFile) CountRemaining() error {
	if *minimumCount < 1 {
		return nil
	}
	c := len(sf.Sites)
	if c < *minimumCount {
		return fmt.Errorf("Too few sites (%v) validated, wanted %v.  Fix errors or lower --minimum.", c, *minimumCount)
	}
	return nil
}

func CheckHTTP(urlString string) (E error) {

	// Complain about slow urlStrings
	t0 := time.Now()
	defer func() {
		if E == nil {
			t1 := time.Now()
			td := t1.Sub(t0)
			if td > (*slow) {
				log.Printf("Slow! %s %s\n", td, urlString)
			}
		}
	}()

	client := &http.Client{
		Timeout: time.Duration(15) * time.Second,
	}

	var err error
	var resp *http.Response

	if *validator == "" {
		resp, err = client.Get(urlString)
		if err != nil {
			return err
		}
	} else {
		form := url.Values{}
		form.Add("url", urlString)
		req, err := http.NewRequest("POST", *validator, strings.NewReader(form.Encode()))
		resp, err = client.Do(req)
		if err != nil {
			return err
		}
	}

	// Start the fetch
	defer resp.Body.Close()

	// Finish the fetch
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("%s: %s", urlString, resp.Status)
	}

	// Check the content type
	ct := resp.Header.Get("Content-Type")
	if ct == "" {
		return fmt.Errorf("%s: No Content-Type in response", urlString)
	}
	ctl := strings.ToLower(ct)

	// If we like the content type, return success
	if ctl == "image/gif" ||
		ctl == "image/jpg" ||
		ctl == "image/jpeg" ||
		ctl == "image/png" {
		return nil
	}

	// Otherwise complain
	return fmt.Errorf("%s: Bad Content-Type: %s", urlString, ct)

}

// CheckHTTP checks if a given URL is good or not;
// and if not good, marks  SiteRecord.Hide=1
func (sr *SiteRecord) CheckHTTP(wg *sync.WaitGroup) {
	// Mark the goroutine as done on function exit
	defer wg.Done()

	// Try IPv4 then IPv6.
	// Don't do these in parallel with each other;
	// we don't want to deal with lock issues
	// on sr.Reason .  So, do them in serial.
	if err4 := CheckHTTP(sr.V4); err4 != nil {
		sr.Reason = err4.Error()
		sr.Hide = true
		log.Println(sr.Reason)
		return
	}
	if err6 := CheckHTTP(sr.V6); err6 != nil {
		sr.Reason = err6.Error()
		sr.Hide = true
		log.Println(sr.Reason)
		return
	}

}

// CheckMIRROR checks if a mirror's urls are good - a subset of
// a full verification.  TODO: Make a lib that does full site verification.
func (sr *SiteRecord) CheckMIRROR(domain string, wg *sync.WaitGroup) {
	// Mark the goroutine as done on function exit
	defer wg.Done()

	// Try IPv4 then IPv6.
	// Don't do these in parallel with each other;
	// we don't want to deal with lock issues
	// on sr.Reason .  So, do them in serial.
	patterns := []string{"http://ipv4.%s/images-nc/knob_green.png", "http://ipv6.%s/images-nc/knob_green.png", "http://ds.v6ns.%s/images-nc/knob_green.png", "http://mtu1280.%s/images-nc/knob_green.png"}

	for _, pattern := range patterns {
		try := fmt.Sprintf(pattern, domain)
		if err := CheckHTTP(try); err != nil {
			sr.Reason = err.Error()
			sr.Hide = true
			log.Println(sr.Reason)
			return
		}
	}
}

// CheckHTTP against the entire SitesFile
func (sf *SitesFile) CheckHTTP() {
	// Each site, we will spin off as a goroutine.
	// When we do so, we need to track their finish.
	wg := &sync.WaitGroup{}

	for domain, sr := range sf.Sites {
		if sr.Hide == false {
			wg.Add(1)
			if sr.Mirror {
				go sr.CheckMIRROR(domain, wg)
			} else {
				go sr.CheckHTTP(wg)
			}
			time.Sleep(20 * time.Millisecond)
		}
	}
	wg.Wait()
}

func main() {
	flag.Parse()
	sf, err := ReadSitesFile(*input)
	if err != nil {
		log.Fatal(err)
	}
	sf.FixDefaults()  // Fix the v4 and v6 test urls if blank
	sf.DeleteHidden() // First pass cleanup, stuff humans marked hide

	log.Printf("Checking %v sites\n", len(sf.Sites))

	sf.CheckHTTP()    // Do active checks against the v4 and v6 test urls
	sf.DeleteHidden() // Second pass cleanup, stuff CheckHTTP marked hide

	log.Printf("After checking, %v sites remain\n", len(sf.Sites))

	if err = sf.CountRemaining(); err != nil {
		log.Fatal(err)
	}

	err = sf.Sites.WriteJS(*parsed, "GIGO.sites_parsed=", ";")
	if err != nil {
		log.Fatal(err)
	}
	err = sf.Sites.WriteJS(*raw, "", "")
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Use beta validator<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

var input = flag.String("input", "sites.json", "json file to read")
var parsed = flag.String("parsed", "../templates/js/sites_parsed.js", "GIGO.sites_parsed= js file to write for falling-sky")
var raw = flag.String("raw", "../templates/js/sites_parsed_raw.js", "js file to write for other automation")
var validator = flag.String("validator", "http://beta.validator.test-ipv6.com/v0/CheckMirror/Check", "use this validator api")
var slow = flag.Duration("slow", time.Second*12, "time to consider a lookup 'slow'") // TODO Make this slow check per-url?
var minimumCount = flag.Int("minimum", 0, "Minimum number of sites that must answer, else fail")

// SiteRecord describes a single mirror or "Other Sites" record
type SiteRecord struct {
	Site        string `json:"site"`        // What site name to publicly attribute
	Mirror      bool   `json:"mirror"`      // if true, site is a full mirror; if false, just "OtherSites"
	Hide        bool   `json:"hide"`        // If true, stop sending traffic here
	V4          string `json:"v4"`          // IPv4 test URL  (http or https)
	V6          string `json:"v6"`          // IPv6 test URL (http or https)
	Loc         string `json:"loc"`         // Country where the site is located
	Provider    string `json:"provider"`    // What provider to publicly attribute
	Monitor     string `json:"monitor"`     // Who to notify on error (automation)
	Contact     string `json:"contact"`     // Who to notify on error (human)
	Reason      string `json:"reason"`      // Reason disabled
	Transparent bool   `json:"transparent"` // Whether or not the site is a transparent mirror
}

// SitesMap is a series of site records, keyed by site name
type SitesMap map[string]*SiteRecord

// SitesFile is a container, containing a SitesMap
// This works around the problems with Go unmarshalling to a top level map
type SitesFile struct {
	Sites SitesMap `json:"sites"`
}

// Synced from validator service

// CheckMirrorRequest descripts what users can ask of us
type CheckMirrorRequest struct {
	Mirror       string   `json:"mirror"`               // mirror name sent on Start
	Transparent  bool     `json:"transparent"`          // Do transparent checks too
	Session      string   `json:"session"`              // Created on Start; sent back on followups
	ResourceV4   string   `json:"resource_v4"`          // IF not a mirror.. do a resource check. resource_v4 and resource_v6
	ResourceV6   string   `json:"resource_v6"`          // IF not a mirror.. do a resource check. resource_v4 and resource_v6
	TestNames    []string `json:"test_names,omitempty"` // What names do you want?
	WantMarkdown bool     `json:"want_markdown"`
}

// CheckMirrorResponse describes what we give back to the API consumer
type CheckMirrorResponse struct {
	Mirror  string                 `json:"mirror,omitempty"`  // mirror name sent on Start
	Session string                 `json:"session,omitempty"` // Created on Start; sent back on followups
	Status  map[string]*StatusType `json:"status,omitempty"`
	Details map[string]*DetailType `json:"details,omitempty"`
	Done    bool                   `json:"done"`  // If true, stop polling.
	Error   string                 `json:"error"` // Any error we want displayed
}

// StatusType gives the short version of what's going on with all tests.
// Whenever asked, we'll give the dump of this.
// Worst case: 50 tests times 100 bytes? = 5k
type StatusType struct {
	TestName    string   `json:"test_name"`
	Hide        bool     `json:"hide"`        // Not sure that need this yet
	Description string   `json:"description"` // 1-liner
	Status      string   `json:"status"`      // OK BAD WARNING SKIPPED
	DependsOn   []string `json:"depends_on"`  // List of things that (if bad) should half-hide this
}

// DetailType provides details of specific tests
type DetailType struct {
	TestName     string   `json:"test_name"`
	Hide         bool     `json:"hide"`          // Not sure that need this yet
	Description  string   `json:"description"`   // 1-liner
	Status       string   `json:"status"`        // OK BAD WARNING SKIPPED
	DependsOn    []string `json:"depends_on"`    // List of things that (if bad) should half-hide this
	Expected     string   `json:"expected"`      // What we expected when checking
	ExpectedHTML string   `json:"expected_html"` // Expected, rendered
	Found        string   `json:"found"`         // What we found when checking
	FoundHTML    string   `json:"found_html"`    // found, rendered
	Help         string   `json:"help"`          // Big block of text for user
	HelpHTML     string   `json:"help_html"`     // Rendered help
}

// ReadSitesFile reads from disk
func ReadSitesFile(fn string) (*SitesFile, error) {
	var sf SitesFile

	b, err := ioutil.ReadFile(fn)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(b, &sf)
	if err != nil {
		return nil, err
	}

	return &sf, nil
}

func (sm *SitesMap) WriteJS(fn string, prefix string, suffix string) error {
	if fn == "" {
		return nil
	}
	s := prefix + sm.String() + suffix
	b := []byte(s)
	err := ioutil.WriteFile(fn+".new", b, 0755)
	if err != nil {
		return err
	}
	err = os.Rename(fn+".new", fn)
	return err
}

// Bytes returns formatted data
func (sf *SitesFile) Bytes() ([]byte, error) {
	b, err := json.MarshalIndent(sf, "", " ")
	if err != nil {
		log.Fatal(err)
	}
	return b, err
}

// String returns formatted data
func (sf *SitesFile) String() string {
	b, err := sf.Bytes()
	if err != nil {
		log.Fatal(err)
	}
	return string(b)
}

// Bytes returns formatted data
func (sm *SitesMap) Bytes() ([]byte, error) {
	b, err := json.MarshalIndent(sm, "", " ")
	if err != nil {
		log.Fatal(err)
	}
	return b, err
}

// String returns formatted data
func (sm *SitesMap) String() string {
	b, err := sm.Bytes()
	if err != nil {
		log.Fatal(err)
	}
	return string(b)
}

// Print the sites file to stdout
func (sf *SitesFile) Print() {
	fmt.Println(sf.String())
}

// FixDefaults Scans the records and fixes any missing info,
// canonicalizes 1/0 values.
func (sf *SitesFile) FixDefaults() error {
	for key, sr := range sf.Sites {

		// Make sure the "site" is filled out (same as the key)
		if sr.Site == "" {
			sr.Site = key
		}
		// Canonicalize true/false/empty/etc as "1" and "0" strings
	}

	return nil
}

func (sf *SitesFile) DeleteHidden() error {
	for key, sr := range sf.Sites {
		if sr.Hide {
			delete(sf.Sites, key)
		}
	}
	return nil
}
func (sf *SitesFile) CountRemaining() error {
	if *minimumCount < 1 {
		return nil
	}
	c := len(sf.Sites)
	if c < *minimumCount {
		return fmt.Errorf("Too few sites (%v) validated, wanted %v.  Fix errors or lower --minimum.", c, *minimumCount)
	}
	return nil
}

/* TimeoutDialer and NewTimeoutClient are from
   http://stackoverflow.com/a/16930649/2399725
   Thanks go to dmichael for sharing!
*/

func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
	return func(netw, addr string) (net.Conn, error) {
		conn, err := net.DialTimeout(netw, addr, cTimeout)
		if err != nil {
			return nil, err
		}
		conn.SetDeadline(time.Now().Add(rwTimeout))
		return conn, nil
	}
}

func NewTimeoutClient(connectTimeout time.Duration, readWriteTimeout time.Duration) *http.Client {

	return &http.Client{
		Transport: &http.Transport{
			Dial: TimeoutDialer(connectTimeout, readWriteTimeout),
		},
	}
}

func (cmr CheckMirrorRequest) String() string {
	b, e := json.Marshal(cmr)
	if e == nil {
		return string(b)
	}
	return fmt.Sprintf("%#v", cmr)
}

func (i CheckMirrorResponse) String() string {
	b, e := json.Marshal(i)
	if e == nil {
		return string(b)
	}
	return fmt.Sprintf("%#v", i)
}

func (i StatusType) String() string {
	b, e := json.Marshal(i)
	if e == nil {
		return string(b)
	}
	return fmt.Sprintf("%#v", i)
}

func (i DetailType) String() string {
	b, e := json.Marshal(i)
	if e == nil {
		return string(b)
	}
	return fmt.Sprintf("%#v", i)
}

func (sr *SiteRecord) CheckVerifier(domain string, wg *sync.WaitGroup) (err error) {
	var jsonStr string
	defer wg.Done()
	defer func() {
		if err != nil {
			sr.Hide = true
			sr.Reason = err.Error()
			log.Printf("Dropping %s: %s\n", domain, sr.Reason)
			if jsonStr != "" {
				//	log.Print(jsonStr)
			}
		}
	}()

	cmr := CheckMirrorRequest{}

	if sr.Mirror {
		cmr.Mirror = domain
		cmr.Transparent = sr.Transparent
		sr.V4 = fmt.Sprintf("http://ipv4.%s/images-nc/knob_green.png", domain)
		sr.V6 = fmt.Sprintf("http://ipv6.%s/images-nc/knob_green.png", domain)
	} else {
		cmr.ResourceV4 = sr.V4
		cmr.ResourceV6 = sr.V6
	}

	cmr.WantMarkdown = false

	jsonStr = cmr.String()
	//log.Print(payload)
	payloadBuf := bytes.NewBufferString(jsonStr)
	client := NewTimeoutClient(time.Second*40, time.Second*40)
	req, err := http.NewRequest("POST", *validator, payloadBuf)
	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	// Start the fetch
	defer resp.Body.Close()

	// Finish the fetch
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		//log.Printf("%s: %s after POST with: %s\n", *validator, resp.Status, jsonStr)
		return fmt.Errorf("%s after POST with: %s", resp.Status, jsonStr)
	}

	cmres := CheckMirrorResponse{}
	err = json.Unmarshal(body, &cmres)
	if err != nil {
		//	log.Printf("%s: decode error after POST with: %s\n", *validator, jsonStr)
		return fmt.Errorf("decode error after POST with: %s", jsonStr)
	}

	//log.Print(cmres.String())

	bad := []string{}
	for _, test := range cmres.Status {
		if test.Status != "OK" && test.Status != "SKIPPED" && test.Status != "WARNING" {
			log.Printf("%s %s %s %s\n", domain, test.TestName, test.Status, test.Description)
			bad = append(bad, test.TestName)
		}
	}
	if len(bad) > 0 {
		return fmt.Errorf("Bad: %s", strings.Join(bad, "; "))
	}
	return nil
}

// CheckHTTP against the entire SitesFile
func (sf *SitesFile) CheckHTTP() {
	// Each site, we will spin off as a goroutine.
	// When we do so, we need to track their finish.
	wg := &sync.WaitGroup{}

	for domain, sr := range sf.Sites {
		if sr.Hide == false {
			wg.Add(1)
			go sr.CheckVerifier(domain, wg)
			time.Sleep(1 * time.Second / 10)
		}
	}
	wg.Wait()
}

func main() {
	flag.Parse()
	sf, err := ReadSitesFile(*input)
	if err != nil {
		log.Fatal(err)
	}
	sf.FixDefaults()  // Fix the v4 and v6 test urls if blank
	sf.DeleteHidden() // First pass cleanup, stuff humans marked hide

	log.Printf("Checking %v sites\n", len(sf.Sites))

	sf.CheckHTTP()    // Do active checks against the v4 and v6 test urls
	sf.DeleteHidden() // Second pass cleanup, stuff CheckHTTP marked hide

	log.Printf("After checking, %v sites remain\n", len(sf.Sites))

	if err = sf.CountRemaining(); err != nil {
		log.Fatal(err)
	}

	err = sf.Sites.WriteJS(*parsed, "GIGO.sites_parsed=", ";")
	if err != nil {
		log.Fatal(err)
	}
	err = sf.Sites.WriteJS(*raw, "", "")
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>