<commit_before>// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/version.go
// Copyright (C) 2015-2022 The Lightning Network Developers

package build

import (
	"fmt"
	"strings"
)

var (
	// Commit stores the current commit of this build, which includes the
	// most recent tag, the number of commits since that tag (if non-zero),
	// the commit hash, and a dirty marker. This should be set using the
	// -ldflags during compilation.
	Commit string

	// CommitHash stores the current commit hash of this build, this should
	// be set using the -ldflags during compilation.
	CommitHash string

	// RawTags contains the raw set of build tags, separated by commas. This
	// should be set using -ldflags during compilation.
	RawTags string

	// GoVersion stores the go version that the executable was compiled
	// with. This should be set using -ldflags during compilation.
	GoVersion string
)

// semanticAlphabet is the set of characters that are permitted for use in an
// AppPreRelease.
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."

// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
	// AppMajor defines the major version of this binary.
	AppMajor uint = 0

	// AppMinor defines the minor version of this binary.
	AppMinor uint = 15

	// AppPatch defines the application patch for this binary.
	AppPatch uint = 0

	// AppPreRelease MUST only contain characters from semanticAlphabet
	// per the semantic versioning spec.
	AppPreRelease = "beta.rc4"
)

func init() {
	// Assert that AppPreRelease is valid according to the semantic
	// versioning guidelines for pre-release version and build metadata
	// strings. In particular it MUST only contain characters in
	// semanticAlphabet.
	for _, r := range AppPreRelease {
		if !strings.ContainsRune(semanticAlphabet, r) {
			panic(fmt.Errorf("rune: %v is not in the semantic "+
				"alphabet", r))
		}
	}
}

// Version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func Version() string {
	// Start with the major, minor, and patch versions.
	version := fmt.Sprintf("%d.%d.%d", AppMajor, AppMinor, AppPatch)

	// Append pre-release version if there is one. The hyphen called for by
	// the semantic versioning spec is automatically appended and should not
	// be contained in the pre-release string.
	if AppPreRelease != "" {
		version = fmt.Sprintf("%s-%s", version, AppPreRelease)
	}

	return version
}

// Tags returns the list of build tags that were compiled into the executable.
func Tags() []string {
	if len(RawTags) == 0 {
		return nil
	}

	return strings.Split(RawTags, ",")
}
<commit_msg>build: bump version to v0.15.0-beta.rc5<commit_after>// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/version.go
// Copyright (C) 2015-2022 The Lightning Network Developers

package build

import (
	"fmt"
	"strings"
)

var (
	// Commit stores the current commit of this build, which includes the
	// most recent tag, the number of commits since that tag (if non-zero),
	// the commit hash, and a dirty marker. This should be set using the
	// -ldflags during compilation.
	Commit string

	// CommitHash stores the current commit hash of this build, this should
	// be set using the -ldflags during compilation.
	CommitHash string

	// RawTags contains the raw set of build tags, separated by commas. This
	// should be set using -ldflags during compilation.
	RawTags string

	// GoVersion stores the go version that the executable was compiled
	// with. This should be set using -ldflags during compilation.
	GoVersion string
)

// semanticAlphabet is the set of characters that are permitted for use in an
// AppPreRelease.
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."

// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
	// AppMajor defines the major version of this binary.
	AppMajor uint = 0

	// AppMinor defines the minor version of this binary.
	AppMinor uint = 15

	// AppPatch defines the application patch for this binary.
	AppPatch uint = 0

	// AppPreRelease MUST only contain characters from semanticAlphabet
	// per the semantic versioning spec.
	AppPreRelease = "beta.rc5"
)

func init() {
	// Assert that AppPreRelease is valid according to the semantic
	// versioning guidelines for pre-release version and build metadata
	// strings. In particular it MUST only contain characters in
	// semanticAlphabet.
	for _, r := range AppPreRelease {
		if !strings.ContainsRune(semanticAlphabet, r) {
			panic(fmt.Errorf("rune: %v is not in the semantic "+
				"alphabet", r))
		}
	}
}

// Version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func Version() string {
	// Start with the major, minor, and patch versions.
	version := fmt.Sprintf("%d.%d.%d", AppMajor, AppMinor, AppPatch)

	// Append pre-release version if there is one. The hyphen called for by
	// the semantic versioning spec is automatically appended and should not
	// be contained in the pre-release string.
	if AppPreRelease != "" {
		version = fmt.Sprintf("%s-%s", version, AppPreRelease)
	}

	return version
}

// Tags returns the list of build tags that were compiled into the executable.
func Tags() []string {
	if len(RawTags) == 0 {
		return nil
	}

	return strings.Split(RawTags, ",")
}
<|endoftext|>


<commit_before>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package juju provide utilities functions for interaction with Juju. It also
// provides a provisioner implementation for Juju.
package juju
<commit_msg>provision/juju: improve docs<commit_after>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package juju provide utilities functions for interaction with Juju. It also
// provides a provisioner implementation for Juju.
//
// In order to use the provisioner, just import tsuru's provision package and
// juju provision package. Then call provision.Get("juju") to get an instance
// of JujuProvisioner:
//
//     import (
//         "github.com/globocom/tsuru/provision"
//         _ "github.com/globocom/tsuru/provision/juju"
//     )
//     // ...
//     func main() {
//         provisioner, err := provision.Get("juju")
//         // Use provisioner.
//     }
package juju
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes 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 validation

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/yaml"
	"github.com/emicklei/go-restful/swagger"
	"github.com/golang/glog"
)

type InvalidTypeError struct {
	ExpectedKind reflect.Kind
	ObservedKind reflect.Kind
	FieldName    string
}

func (i *InvalidTypeError) Error() string {
	return fmt.Sprintf("expected type %s, for field %s, got %s", i.ExpectedKind.String(), i.FieldName, i.ObservedKind.String())
}

func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName string) error {
	return &InvalidTypeError{expected, observed, fieldName}
}

// Schema is an interface that knows how to validate an API object serialized to a byte array.
type Schema interface {
	ValidateBytes(data []byte) error
}

type NullSchema struct{}

func (NullSchema) ValidateBytes(data []byte) error { return nil }

type SwaggerSchema struct {
	api swagger.ApiDeclaration
}

func NewSwaggerSchemaFromBytes(data []byte) (Schema, error) {
	schema := &SwaggerSchema{}
	err := json.Unmarshal(data, &schema.api)
	if err != nil {
		return nil, err
	}
	return schema, nil
}

func (s *SwaggerSchema) ValidateBytes(data []byte) error {
	var obj interface{}
	out, err := yaml.ToJSON(data)
	if err != nil {
		return err
	}
	data = out
	if err := json.Unmarshal(data, &obj); err != nil {
		return err
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return fmt.Errorf("error in unmarshaling data %s", string(data))
	}
	apiVersion := fields["apiVersion"].(string)
	kind := fields["kind"].(string)
	return s.ValidateObject(obj, apiVersion, "", apiVersion+"."+kind)
}

func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, typeName string) error {
	models := s.api.Models
	// TODO: handle required fields here too.
	model, ok := models[typeName]
	if !ok {
		return fmt.Errorf("couldn't find type: %s", typeName)
	}
	properties := model.Properties
	if len(properties) == 0 {
		// The object does not have any sub-fields.
		return nil
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return fmt.Errorf("expected object of type map[string]interface{} as value of %s field", fieldName)
	}
	if len(fieldName) > 0 {
		fieldName = fieldName + "."
	}
	for key, value := range fields {
		details, ok := properties[key]
		if !ok {
			glog.Infof("unknown field: %s", key)
			// Some properties can be missing because of
			// https://github.com/GoogleCloudPlatform/kubernetes/issues/6842.
			glog.Info("this may be a false alarm, see https://github.com/GoogleCloudPlatform/kubernetes/issues/6842")
			continue
		}
		if details.Type == nil && details.Ref == nil {
			return fmt.Errorf("could not find the type of %s from object: %v", key, details)
		}
		var fieldType string
		if details.Type != nil {
			fieldType = *details.Type
		} else {
			fieldType = *details.Ref
		}
		if value == nil {
			glog.V(2).Infof("Skipping nil field: %s", key)
			continue
		}
		err := s.validateField(value, apiVersion, fieldName+key, fieldType, &details)
		if err != nil {
			glog.Errorf("Validation failed for: %s, %v", key, value)
			return err
		}
	}
	return nil
}

func (s *SwaggerSchema) validateField(value interface{}, apiVersion, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) error {
	if strings.HasPrefix(fieldType, apiVersion) {
		return s.ValidateObject(value, apiVersion, fieldName, fieldType)
	}
	switch fieldType {
	case "string":
		// Be loose about what we accept for 'string' since we use IntOrString in a couple of places
		_, isString := value.(string)
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isString && !isNumber && !isInteger {
			return NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName)
		}
	case "array":
		arr, ok := value.([]interface{})
		if !ok {
			return NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName)
		}
		var arrType string
		glog.Infof("field detail %v", fieldDetails)
		if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil {
			return NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName)
		}
		if fieldDetails.Items.Ref != nil {
			arrType = *fieldDetails.Items.Ref
		} else {
			arrType = *fieldDetails.Items.Type
		}
		for ix := range arr {
			err := s.validateField(arr[ix], apiVersion, fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil)
			if err != nil {
				return err
			}
		}
	case "uint64":
	case "int64":
	case "integer":
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isNumber && !isInteger {
			return NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName)
		}
	case "float64":
		if _, ok := value.(float64); !ok {
			return NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName)
		}
	case "boolean":
		if _, ok := value.(bool); !ok {
			return NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName)
		}
	case "any":
	default:
		return fmt.Errorf("unexpected type: %v", fieldType)
	}
	return nil
}
<commit_msg>Update go-restful to get stable sorting in spec<commit_after>/*
Copyright 2014 The Kubernetes 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 validation

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/yaml"
	"github.com/emicklei/go-restful/swagger"
	"github.com/golang/glog"
)

type InvalidTypeError struct {
	ExpectedKind reflect.Kind
	ObservedKind reflect.Kind
	FieldName    string
}

func (i *InvalidTypeError) Error() string {
	return fmt.Sprintf("expected type %s, for field %s, got %s", i.ExpectedKind.String(), i.FieldName, i.ObservedKind.String())
}

func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName string) error {
	return &InvalidTypeError{expected, observed, fieldName}
}

// Schema is an interface that knows how to validate an API object serialized to a byte array.
type Schema interface {
	ValidateBytes(data []byte) error
}

type NullSchema struct{}

func (NullSchema) ValidateBytes(data []byte) error { return nil }

type SwaggerSchema struct {
	api swagger.ApiDeclaration
}

func NewSwaggerSchemaFromBytes(data []byte) (Schema, error) {
	schema := &SwaggerSchema{}
	err := json.Unmarshal(data, &schema.api)
	if err != nil {
		return nil, err
	}
	return schema, nil
}

func (s *SwaggerSchema) ValidateBytes(data []byte) error {
	var obj interface{}
	out, err := yaml.ToJSON(data)
	if err != nil {
		return err
	}
	data = out
	if err := json.Unmarshal(data, &obj); err != nil {
		return err
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return fmt.Errorf("error in unmarshaling data %s", string(data))
	}
	apiVersion := fields["apiVersion"].(string)
	kind := fields["kind"].(string)
	return s.ValidateObject(obj, apiVersion, "", apiVersion+"."+kind)
}

func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, typeName string) error {
	models := s.api.Models
	// TODO: handle required fields here too.
	model, ok := models.At(typeName)
	if !ok {
		return fmt.Errorf("couldn't find type: %s", typeName)
	}
	properties := model.Properties
	if len(properties.List) == 0 {
		// The object does not have any sub-fields.
		return nil
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return fmt.Errorf("expected object of type map[string]interface{} as value of %s field", fieldName)
	}
	if len(fieldName) > 0 {
		fieldName = fieldName + "."
	}
	for key, value := range fields {
		details, ok := properties.At(key)
		if !ok {
			glog.Infof("unknown field: %s", key)
			// Some properties can be missing because of
			// https://github.com/GoogleCloudPlatform/kubernetes/issues/6842.
			glog.Info("this may be a false alarm, see https://github.com/GoogleCloudPlatform/kubernetes/issues/6842")
			continue
		}
		if details.Type == nil && details.Ref == nil {
			return fmt.Errorf("could not find the type of %s from object: %v", key, details)
		}
		var fieldType string
		if details.Type != nil {
			fieldType = *details.Type
		} else {
			fieldType = *details.Ref
		}
		if value == nil {
			glog.V(2).Infof("Skipping nil field: %s", key)
			continue
		}
		err := s.validateField(value, apiVersion, fieldName+key, fieldType, &details)
		if err != nil {
			glog.Errorf("Validation failed for: %s, %v", key, value)
			return err
		}
	}
	return nil
}

func (s *SwaggerSchema) validateField(value interface{}, apiVersion, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) error {
	if strings.HasPrefix(fieldType, apiVersion) {
		return s.ValidateObject(value, apiVersion, fieldName, fieldType)
	}
	switch fieldType {
	case "string":
		// Be loose about what we accept for 'string' since we use IntOrString in a couple of places
		_, isString := value.(string)
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isString && !isNumber && !isInteger {
			return NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName)
		}
	case "array":
		arr, ok := value.([]interface{})
		if !ok {
			return NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName)
		}
		var arrType string
		glog.Infof("field detail %v", fieldDetails)
		if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil {
			return NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName)
		}
		if fieldDetails.Items.Ref != nil {
			arrType = *fieldDetails.Items.Ref
		} else {
			arrType = *fieldDetails.Items.Type
		}
		for ix := range arr {
			err := s.validateField(arr[ix], apiVersion, fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil)
			if err != nil {
				return err
			}
		}
	case "uint64":
	case "int64":
	case "integer":
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isNumber && !isInteger {
			return NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName)
		}
	case "float64":
		if _, ok := value.(float64); !ok {
			return NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName)
		}
	case "boolean":
		if _, ok := value.(bool); !ok {
			return NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName)
		}
	case "any":
	default:
		return fmt.Errorf("unexpected type: %v", fieldType)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 The Kubeflow 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 v1

import (
	common "github.com/kubeflow/tf-operator/pkg/apis/common/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +resource:path=pytorchjob

// PyTorchJob represents the configuration of PyTorchJob
type PyTorchJob struct {
	metav1.TypeMeta `json:",inline"`

	// Standard object's metadata.
	metav1.ObjectMeta `json:"metadata,omitempty"`

	// Specification of the desired behavior of the PyTorchJob.
	Spec PyTorchJobSpec `json:"spec,omitempty"`

	// Most recently observed status of the PyTorchJob.
	// This data may not be up to date.
	// Populated by the system.
	// Read-only.
	Status common.JobStatus `json:"status,omitempty"`
}

// PyTorchJobSpec is a desired state description of the PyTorchJob.
type PyTorchJobSpec struct {
	// Specifies the duration in seconds relative to the startTime that the job may be active
	// before the system tries to terminate it; value must be positive integer.
	// This method applies only to pods with restartPolicy == OnFailure or Always.
	// +optional
	ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`

	// Optional number of retries before marking this job failed.
	// +optional
	BackoffLimit *int32 `json:"backoffLimit,omitempty"`

	// CleanPodPolicy defines the policy to kill pods after PyTorchJob is
	// succeeded.
	// Default to Running.
	CleanPodPolicy *common.CleanPodPolicy `json:"cleanPodPolicy,omitempty"`

	// TTLSecondsAfterFinished is the TTL to clean up pytorch-jobs (temporary
	// before kubernetes adds the cleanup controller).
	// It may take extra ReconcilePeriod seconds for the cleanup, since
	// reconcile gets called periodically.
	// Default to infinite.
	TTLSecondsAfterFinished *int32 `json:"ttlSecondsAfterFinished,omitempty"`

	// PyTorchReplicaSpecs is map of PyTorchReplicaType and PyTorchReplicaSpec
	// specifies the PyTorch replicas to run.
	// For example,
	//   {
	//     "Master": PyTorchReplicaSpec,
	//     "Worker": PyTorchReplicaSpec,
	//   }
	PyTorchReplicaSpecs map[PyTorchReplicaType]*common.ReplicaSpec `json:"pytorchReplicaSpecs"`
}

// PyTorchReplicaType is the type for PyTorchReplica.
type PyTorchReplicaType common.ReplicaType

const (
	// PyTorchReplicaTypeMaster is the type of Master of distributed PyTorch
	PyTorchReplicaTypeMaster PyTorchReplicaType = "Master"

	// PyTorchReplicaTypeWorker is the type for workers of distributed PyTorch.
	PyTorchReplicaTypeWorker PyTorchReplicaType = "Worker"
)

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +resource:path=pytorchjobs

// PyTorchJobList is a list of PyTorchJobs.
type PyTorchJobList struct {
	metav1.TypeMeta `json:",inline"`

	// Standard list metadata.
	metav1.ListMeta `json:"metadata,omitempty"`

	// List of PyTorchJobs.
	Items []PyTorchJob `json:"items"`
}
<commit_msg>Polish documentation for PyTorch V1 (#167)<commit_after>// Copyright 2019 The Kubeflow 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 v1

import (
	common "github.com/kubeflow/tf-operator/pkg/apis/common/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +resource:path=pytorchjob

// Represents a PyTorchJob resource.
type PyTorchJob struct {
	// Standard Kubernetes type metadata.
	metav1.TypeMeta `json:",inline"`

	// Standard Kubernetes object's metadata.
	metav1.ObjectMeta `json:"metadata,omitempty"`

	// Specification of the desired state of the PyTorchJob.
	Spec PyTorchJobSpec `json:"spec,omitempty"`

	// Most recently observed status of the PyTorchJob.
	// Read-only (modified by the system).
	Status common.JobStatus `json:"status,omitempty"`
}

// PyTorchJobSpec is a desired state description of the PyTorchJob.
type PyTorchJobSpec struct {
	// Specifies the duration (in seconds) since startTime during which the job can remain active
	// before it is terminated. Must be a positive integer.
	// This setting applies only to pods where restartPolicy is OnFailure or Always.
	// +optional
	ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`

	// Number of retries before marking this job as failed.
	// +optional
	BackoffLimit *int32 `json:"backoffLimit,omitempty"`

	// Defines the policy for cleaning up pods after the PyTorchJob completes.
	// Defaults to Running.
	CleanPodPolicy *common.CleanPodPolicy `json:"cleanPodPolicy,omitempty"`

	// Defines the TTL for cleaning up finished PyTorchJobs (temporary
	// before Kubernetes adds the cleanup controller).
	// It may take extra ReconcilePeriod seconds for the cleanup, since
	// reconcile gets called periodically.
	// Defaults to infinite.
	TTLSecondsAfterFinished *int32 `json:"ttlSecondsAfterFinished,omitempty"`

	// A map of PyTorchReplicaType (type) to ReplicaSpec (value). Specifies the PyTorch cluster configuration.
	// For example,
	//   {
	//     "Master": PyTorchReplicaSpec,
	//     "Worker": PyTorchReplicaSpec,
	//   }
	PyTorchReplicaSpecs map[PyTorchReplicaType]*common.ReplicaSpec `json:"pytorchReplicaSpecs"`
}

// PyTorchReplicaType is the type for PyTorchReplica. Can be one of "Master" or "Worker".
type PyTorchReplicaType common.ReplicaType

const (
	// PyTorchReplicaTypeMaster is the type of Master of distributed PyTorch
	PyTorchReplicaTypeMaster PyTorchReplicaType = "Master"

	// PyTorchReplicaTypeWorker is the type for workers of distributed PyTorch.
	PyTorchReplicaTypeWorker PyTorchReplicaType = "Worker"
)

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +resource:path=pytorchjobs

// PyTorchJobList is a list of PyTorchJobs.
type PyTorchJobList struct {
	// Standard type metadata.
	metav1.TypeMeta `json:",inline"`

	// Standard list metadata.
	metav1.ListMeta `json:"metadata,omitempty"`

	// List of PyTorchJobs.
	Items []PyTorchJob `json:"items"`
}
<|endoftext|>


<commit_before>// Copyright 2018 Authors of Cilium
//
// 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 cgroups

import (
	"fmt"
	"os"

	"github.com/cilium/cilium/pkg/mountinfo"

	"golang.org/x/sys/unix"
)

// mountCgroup mounts the Cgroup v2 filesystem into the desired cgroupRoot directory.
func mountCgroup() error {
	cgroupRootStat, err := os.Stat(cgroupRoot)
	if err != nil {
		if os.IsNotExist(err) {
			if err := os.MkdirAll(cgroupRoot, 0755); err != nil {
				return fmt.Errorf("Unable to create cgroup mount directory: %s", err)
			}
		} else {
			return fmt.Errorf("Failed to stat the mount path %s: %s", cgroupRoot, err)
		}
	} else if !cgroupRootStat.IsDir() {
		return fmt.Errorf("%s is a file which is not a directory", cgroupRoot)
	}

	if err := unix.Mount("none", cgroupRoot, "cgroup2", 0, ""); err != nil {
		return fmt.Errorf("failed to mount %s: %s", cgroupRoot, err)
	}

	return nil
}

// checkOrMountCustomLocation tries to check or mount the BPF filesystem in the
// given path.
func cgrpCheckOrMountLocation(cgroupRoot string) error {
	setCgroupRoot(cgroupRoot)

	// Check whether the custom location has a mount.
	mounted, cgroupInstance, err := mountinfo.IsMountFS(mountinfo.FilesystemTypeCgroup2, cgroupRoot)
	if err != nil {
		return err
	}

	// If the custom location has no mount, let's mount there.
	if !mounted {
		if err := mountCgroup(); err != nil {
			return err
		}
	}

	if !cgroupInstance {
		return fmt.Errorf("Mount in the custom directory %s has a different filesystem than cgroup2", cgroupRoot)
	}
	return nil
}
<commit_msg>pkg/cgroup: Fix a typo<commit_after>// Copyright 2018 Authors of Cilium
//
// 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 cgroups

import (
	"fmt"
	"os"

	"github.com/cilium/cilium/pkg/mountinfo"

	"golang.org/x/sys/unix"
)

// mountCgroup mounts the Cgroup v2 filesystem into the desired cgroupRoot directory.
func mountCgroup() error {
	cgroupRootStat, err := os.Stat(cgroupRoot)
	if err != nil {
		if os.IsNotExist(err) {
			if err := os.MkdirAll(cgroupRoot, 0755); err != nil {
				return fmt.Errorf("Unable to create cgroup mount directory: %s", err)
			}
		} else {
			return fmt.Errorf("Failed to stat the mount path %s: %s", cgroupRoot, err)
		}
	} else if !cgroupRootStat.IsDir() {
		return fmt.Errorf("%s is a file which is not a directory", cgroupRoot)
	}

	if err := unix.Mount("none", cgroupRoot, "cgroup2", 0, ""); err != nil {
		return fmt.Errorf("failed to mount %s: %s", cgroupRoot, err)
	}

	return nil
}

// checkOrMountCustomLocation tries to check or mount the cgroup filesystem in the
// given path.
func cgrpCheckOrMountLocation(cgroupRoot string) error {
	setCgroupRoot(cgroupRoot)

	// Check whether the custom location has a mount.
	mounted, cgroupInstance, err := mountinfo.IsMountFS(mountinfo.FilesystemTypeCgroup2, cgroupRoot)
	if err != nil {
		return err
	}

	// If the custom location has no mount, let's mount there.
	if !mounted {
		if err := mountCgroup(); err != nil {
			return err
		}
	}

	if !cgroupInstance {
		return fmt.Errorf("Mount in the custom directory %s has a different filesystem than cgroup2", cgroupRoot)
	}
	return nil
}
<|endoftext|>


<commit_before>package builder

// nginx
const (
	NginxVersion           = "1.19.7"
	NginxDownloadURLPrefix = "https://nginx.org/download"
)

// pcre
const (
	PcreVersion           = "8.44"
	PcreDownloadURLPrefix = "https://ftp.pcre.org/pub/pcre"
)

// openssl
const (
	OpenSSLVersion           = "1.1.1h"
	OpenSSLDownloadURLPrefix = "https://www.openssl.org/source"
)

// libressl
const (
	LibreSSLVersion           = "3.0.2"
	LibreSSLDownloadURLPrefix = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL"
)

// zlib
const (
	ZlibVersion           = "1.2.11"
	ZlibDownloadURLPrefix = "https://zlib.net/fossils"
)

// openResty
const (
	OpenRestyVersion           = "1.19.3.1"
	OpenRestyDownloadURLPrefix = "https://openresty.org/download"
)

// tengine
const (
	TengineVersion           = "2.3.2"
	TengineDownloadURLPrefix = "https://tengine.taobao.org/download"
)

// component enumerations
const (
	ComponentNginx = iota
	ComponentOpenResty
	ComponentTengine
	ComponentPcre
	ComponentOpenSSL
	ComponentLibreSSL
	ComponentZlib
	ComponentMax
)
<commit_msg>bumped default openssl version to 1.1.1i.<commit_after>package builder

// nginx
const (
	NginxVersion           = "1.19.7"
	NginxDownloadURLPrefix = "https://nginx.org/download"
)

// pcre
const (
	PcreVersion           = "8.44"
	PcreDownloadURLPrefix = "https://ftp.pcre.org/pub/pcre"
)

// openssl
const (
	OpenSSLVersion           = "1.1.1i"
	OpenSSLDownloadURLPrefix = "https://www.openssl.org/source"
)

// libressl
const (
	LibreSSLVersion           = "3.0.2"
	LibreSSLDownloadURLPrefix = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL"
)

// zlib
const (
	ZlibVersion           = "1.2.11"
	ZlibDownloadURLPrefix = "https://zlib.net/fossils"
)

// openResty
const (
	OpenRestyVersion           = "1.19.3.1"
	OpenRestyDownloadURLPrefix = "https://openresty.org/download"
)

// tengine
const (
	TengineVersion           = "2.3.2"
	TengineDownloadURLPrefix = "https://tengine.taobao.org/download"
)

// component enumerations
const (
	ComponentNginx = iota
	ComponentOpenResty
	ComponentTengine
	ComponentPcre
	ComponentOpenSSL
	ComponentLibreSSL
	ComponentZlib
	ComponentMax
)
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"strings"

	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/network"
	coretesting "github.com/juju/juju/testing"
)

// linkLayerDevicesInternalSuite contains black-box tests for link-layer network
// devices' internals, which do not actually access mongo. The rest of the logic
// is tested in linkLayerDevicesStateSuite.
type linkLayerDevicesInternalSuite struct {
	testing.IsolationSuite
}

var _ = gc.Suite(&linkLayerDevicesInternalSuite{})

func (s *linkLayerDevicesInternalSuite) TestNewLinkLayerDeviceCreatesLinkLayerDevice(c *gc.C) {
	result := newLinkLayerDevice(nil, linkLayerDeviceDoc{})
	c.Assert(result, gc.NotNil)
	c.Assert(result.st, gc.IsNil)
	c.Assert(result.doc, jc.DeepEquals, linkLayerDeviceDoc{})
}

func (s *linkLayerDevicesInternalSuite) TestDocIDIncludesModelUUID(c *gc.C) {
	const localDocID = "foo"
	globalDocID := coretesting.ModelTag.Id() + ":" + localDocID

	result := s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{DocID: localDocID})
	c.Assert(result.DocID(), gc.Equals, globalDocID)

	result = s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{DocID: globalDocID})
	c.Assert(result.DocID(), gc.Equals, globalDocID)
}

func (s *linkLayerDevicesInternalSuite) newLinkLayerDeviceWithDummyState(doc linkLayerDeviceDoc) *LinkLayerDevice {
	// We only need the model UUID set for localID() and docID() to work.
	// The rest is tested in linkLayerDevicesStateSuite.
	dummyState := &State{modelTag: coretesting.ModelTag}
	return newLinkLayerDevice(dummyState, doc)
}

func (s *linkLayerDevicesInternalSuite) TestProviderIDIsEmptyWhenNotSet(c *gc.C) {
	result := s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{})
	c.Assert(result.ProviderID(), gc.Equals, network.Id(""))
}

func (s *linkLayerDevicesInternalSuite) TestProviderIDDoesNotIncludeModelUUIDWhenSet(c *gc.C) {
	const localProviderID = "foo"
	globalProviderID := coretesting.ModelTag.Id() + ":" + localProviderID

	result := s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{ProviderID: localProviderID})
	c.Assert(result.ProviderID(), gc.Equals, network.Id(localProviderID))
	c.Assert(result.localProviderID(), gc.Equals, localProviderID)

	result = s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{ProviderID: globalProviderID})
	c.Assert(result.ProviderID(), gc.Equals, network.Id(localProviderID))
	c.Assert(result.localProviderID(), gc.Equals, localProviderID)
}

func (s *linkLayerDevicesInternalSuite) TestParentDeviceReturnsNoErrorWhenParentNameNotSet(c *gc.C) {
	result := s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{})
	parent, err := result.ParentDevice()
	c.Check(parent, gc.IsNil)
	c.Check(err, jc.ErrorIsNil)
}

func (s *linkLayerDevicesInternalSuite) TestLinkLayerDeviceGlobalKeyHelper(c *gc.C) {
	result := linkLayerDeviceGlobalKey("42", "eno1")
	c.Assert(result, gc.Equals, "m#42#d#eno1")

	result = linkLayerDeviceGlobalKey("", "")
	c.Assert(result, gc.Equals, "")
}

func (s *linkLayerDevicesInternalSuite) TestGlobalKeyMethod(c *gc.C) {
	doc := linkLayerDeviceDoc{
		MachineID: "42",
		Name:      "foo",
	}
	config := s.newLinkLayerDeviceWithDummyState(doc)
	c.Check(config.globalKey(), gc.Equals, "m#42#d#foo")

	config = s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{})
	c.Check(config.globalKey(), gc.Equals, "")
}

func (s *linkLayerDevicesInternalSuite) TestStringIncludesTypeNameAndMachineID(c *gc.C) {
	doc := linkLayerDeviceDoc{
		MachineID: "42",
		Name:      "foo",
		Type:      BondDevice,
	}
	result := s.newLinkLayerDeviceWithDummyState(doc)
	expectedString := `bond device "foo" on machine "42"`

	c.Assert(result.String(), gc.Equals, expectedString)
}

func (s *linkLayerDevicesInternalSuite) TestRemainingSimpleGetterMethods(c *gc.C) {
	doc := linkLayerDeviceDoc{
		Name:        "bond0",
		MachineID:   "99",
		MTU:         uint(9000),
		Type:        BondDevice,
		MACAddress:  "aa:bb:cc:dd:ee:f0",
		IsAutoStart: true,
		IsUp:        true,
		ParentName:  "br-bond0",
	}
	result := s.newLinkLayerDeviceWithDummyState(doc)

	c.Check(result.Name(), gc.Equals, "bond0")
	c.Check(result.MachineID(), gc.Equals, "99")
	c.Check(result.MTU(), gc.Equals, uint(9000))
	c.Check(result.Type(), gc.Equals, BondDevice)
	c.Check(result.MACAddress(), gc.Equals, "aa:bb:cc:dd:ee:f0")
	c.Check(result.IsAutoStart(), jc.IsTrue)
	c.Check(result.IsUp(), jc.IsTrue)
	c.Check(result.ParentName(), gc.Equals, "br-bond0")
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceTypeWithValidValue(c *gc.C) {
	validTypes := []LinkLayerDeviceType{
		LoopbackDevice,
		EthernetDevice,
		VLAN_8021QDevice,
		BondDevice,
		BridgeDevice,
	}

	for _, value := range validTypes {
		result := IsValidLinkLayerDeviceType(string(value))
		c.Check(result, jc.IsTrue)
	}
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceTypeWithInvalidValue(c *gc.C) {
	result := IsValidLinkLayerDeviceType("")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceType("anything")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceType(" ")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceType("unknown")
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWithUnpatchedGOOS(c *gc.C) {
	result := IsValidLinkLayerDeviceName("valid")
	c.Check(result, jc.IsTrue)
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWithValidNamesWhenGOOSIsinux(c *gc.C) {
	s.PatchValue(&runtimeGOOS, "linux") // isolate the test from the host machine OS.

	for i, name := range validUnixDeviceNames {
		c.Logf("test #%d: %q -> valid", i, name)
		result := IsValidLinkLayerDeviceName(name)
		c.Check(result, jc.IsTrue)
	}
}

var validUnixDeviceNames = []string{
	"eth0", "eno1", "br-eth0.123", "tun:1", "bond0.42",
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWithInvalidNamesWhenGOOIsLinux(c *gc.C) {
	s.PatchValue(&runtimeGOOS, "linux") // isolate the test from the host machine OS.

	result := IsValidLinkLayerDeviceName("")
	c.Check(result, jc.IsFalse)

	const tooLongLength = 16
	result = IsValidLinkLayerDeviceName(strings.Repeat("x", tooLongLength))
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("with-hash#")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has spaces")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has\tabs")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has\newline")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has\r")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has\vtab")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName(".")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("..")
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWithValidNamesWhenGOOSNonLinux(c *gc.C) {
	s.PatchValue(&runtimeGOOS, "non-linux") // isolate the test from the host machine OS.
	validDeviceNames := append(validUnixDeviceNames,
		// Windows network device as friendly name and as underlying UUID.
		"Local Area Connection", "{4a62b748-43d0-4136-92e4-22ce7ee31938}",
	)

	for i, name := range validDeviceNames {
		c.Logf("test #%d: %q -> valid", i, name)
		result := IsValidLinkLayerDeviceName(name)
		c.Check(result, jc.IsTrue)
	}
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWhenGOOSNonLinux(c *gc.C) {
	s.PatchValue(&runtimeGOOS, "non-linux") // isolate the test from the host machine OS.

	result := IsValidLinkLayerDeviceName("")
	c.Check(result, jc.IsFalse)

	const wayTooLongLength = 1024
	result = IsValidLinkLayerDeviceName(strings.Repeat("x", wayTooLongLength))
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("hash# not allowed")
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestStringLengthBetweenWhenTooShort(c *gc.C) {
	result := stringLengthBetween("", 1, 2)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("", 1, 1)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("1", 2, 3)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("12", 3, 3)
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestStringLengthBetweenWhenTooLong(c *gc.C) {
	result := stringLengthBetween("1", 0, 0)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("12", 1, 1)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("123", 1, 2)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("123", 0, 1)
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestStringLengthBetweenWhenWithinLimit(c *gc.C) {
	const (
		minLength = 1
		maxLength = 255
	)
	for i := minLength; i <= maxLength; i++ {
		input := strings.Repeat("x", i)
		result := stringLengthBetween(input, minLength, maxLength)
		c.Check(result, jc.IsTrue)
	}
}
<commit_msg>state: Added an internal test case for parseLinkLayerDeviceParentNameAsGlobalKey<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"strings"

	"github.com/juju/errors"
	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/network"
	coretesting "github.com/juju/juju/testing"
)

// linkLayerDevicesInternalSuite contains black-box tests for link-layer network
// devices' internals, which do not actually access mongo. The rest of the logic
// is tested in linkLayerDevicesStateSuite.
type linkLayerDevicesInternalSuite struct {
	testing.IsolationSuite
}

var _ = gc.Suite(&linkLayerDevicesInternalSuite{})

func (s *linkLayerDevicesInternalSuite) TestNewLinkLayerDeviceCreatesLinkLayerDevice(c *gc.C) {
	result := newLinkLayerDevice(nil, linkLayerDeviceDoc{})
	c.Assert(result, gc.NotNil)
	c.Assert(result.st, gc.IsNil)
	c.Assert(result.doc, jc.DeepEquals, linkLayerDeviceDoc{})
}

func (s *linkLayerDevicesInternalSuite) TestDocIDIncludesModelUUID(c *gc.C) {
	const localDocID = "foo"
	globalDocID := coretesting.ModelTag.Id() + ":" + localDocID

	result := s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{DocID: localDocID})
	c.Assert(result.DocID(), gc.Equals, globalDocID)

	result = s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{DocID: globalDocID})
	c.Assert(result.DocID(), gc.Equals, globalDocID)
}

func (s *linkLayerDevicesInternalSuite) newLinkLayerDeviceWithDummyState(doc linkLayerDeviceDoc) *LinkLayerDevice {
	// We only need the model UUID set for localID() and docID() to work.
	// The rest is tested in linkLayerDevicesStateSuite.
	dummyState := &State{modelTag: coretesting.ModelTag}
	return newLinkLayerDevice(dummyState, doc)
}

func (s *linkLayerDevicesInternalSuite) TestProviderIDIsEmptyWhenNotSet(c *gc.C) {
	result := s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{})
	c.Assert(result.ProviderID(), gc.Equals, network.Id(""))
}

func (s *linkLayerDevicesInternalSuite) TestProviderIDDoesNotIncludeModelUUIDWhenSet(c *gc.C) {
	const localProviderID = "foo"
	globalProviderID := coretesting.ModelTag.Id() + ":" + localProviderID

	result := s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{ProviderID: localProviderID})
	c.Assert(result.ProviderID(), gc.Equals, network.Id(localProviderID))
	c.Assert(result.localProviderID(), gc.Equals, localProviderID)

	result = s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{ProviderID: globalProviderID})
	c.Assert(result.ProviderID(), gc.Equals, network.Id(localProviderID))
	c.Assert(result.localProviderID(), gc.Equals, localProviderID)
}

func (s *linkLayerDevicesInternalSuite) TestParentDeviceReturnsNoErrorWhenParentNameNotSet(c *gc.C) {
	result := s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{})
	parent, err := result.ParentDevice()
	c.Check(parent, gc.IsNil)
	c.Check(err, jc.ErrorIsNil)
}

func (s *linkLayerDevicesInternalSuite) TestLinkLayerDeviceGlobalKeyHelper(c *gc.C) {
	result := linkLayerDeviceGlobalKey("42", "eno1")
	c.Assert(result, gc.Equals, "m#42#d#eno1")

	result = linkLayerDeviceGlobalKey("", "")
	c.Assert(result, gc.Equals, "")
}

func (s *linkLayerDevicesInternalSuite) TestGlobalKeyMethod(c *gc.C) {
	doc := linkLayerDeviceDoc{
		MachineID: "42",
		Name:      "foo",
	}
	config := s.newLinkLayerDeviceWithDummyState(doc)
	c.Check(config.globalKey(), gc.Equals, "m#42#d#foo")

	config = s.newLinkLayerDeviceWithDummyState(linkLayerDeviceDoc{})
	c.Check(config.globalKey(), gc.Equals, "")
}

func (s *linkLayerDevicesInternalSuite) TestParseLinkLayerParentNameAsGlobalKey(c *gc.C) {
	for i, test := range []struct {
		about              string
		input              string
		expectedError      string
		expectedMachineID  string
		expectedParentName string
	}{{
		about: "empty input - empty outputs and no error",
		input: "",
	}, {
		about: "name only as input - empty outputs and no error",
		input: "some-parent",
	}, {
		about:              "global key as input - parsed outputs and no error",
		input:              "m#42#d#br-eth1",
		expectedMachineID:  "42",
		expectedParentName: "br-eth1",
	}, {
		about:         "invalid name as input - empty outputs and NotValidError",
		input:         "some name with not enough # in it",
		expectedError: `ParentName "some name with not enough # in it" format not valid`,
	}, {
		about:         "almost a global key as input - empty outputs and NotValidError",
		input:         "x#foo#y#bar",
		expectedError: `ParentName "x#foo#y#bar" format not valid`,
	}} {
		c.Logf("test #%d: %q", i, test.about)
		gotMachineID, gotParentName, gotError := parseLinkLayerDeviceParentNameAsGlobalKey(test.input)
		if test.expectedError != "" {
			c.Check(gotError, gc.ErrorMatches, test.expectedError)
			c.Check(gotError, jc.Satisfies, errors.IsNotValid)
		} else {
			c.Check(gotError, jc.ErrorIsNil)
		}
		c.Check(gotMachineID, gc.Equals, test.expectedMachineID)
		c.Check(gotParentName, gc.Equals, test.expectedParentName)
	}
}

func (s *linkLayerDevicesInternalSuite) TestStringIncludesTypeNameAndMachineID(c *gc.C) {
	doc := linkLayerDeviceDoc{
		MachineID: "42",
		Name:      "foo",
		Type:      BondDevice,
	}
	result := s.newLinkLayerDeviceWithDummyState(doc)
	expectedString := `bond device "foo" on machine "42"`

	c.Assert(result.String(), gc.Equals, expectedString)
}

func (s *linkLayerDevicesInternalSuite) TestRemainingSimpleGetterMethods(c *gc.C) {
	doc := linkLayerDeviceDoc{
		Name:        "bond0",
		MachineID:   "99",
		MTU:         uint(9000),
		Type:        BondDevice,
		MACAddress:  "aa:bb:cc:dd:ee:f0",
		IsAutoStart: true,
		IsUp:        true,
		ParentName:  "br-bond0",
	}
	result := s.newLinkLayerDeviceWithDummyState(doc)

	c.Check(result.Name(), gc.Equals, "bond0")
	c.Check(result.MachineID(), gc.Equals, "99")
	c.Check(result.MTU(), gc.Equals, uint(9000))
	c.Check(result.Type(), gc.Equals, BondDevice)
	c.Check(result.MACAddress(), gc.Equals, "aa:bb:cc:dd:ee:f0")
	c.Check(result.IsAutoStart(), jc.IsTrue)
	c.Check(result.IsUp(), jc.IsTrue)
	c.Check(result.ParentName(), gc.Equals, "br-bond0")
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceTypeWithValidValue(c *gc.C) {
	validTypes := []LinkLayerDeviceType{
		LoopbackDevice,
		EthernetDevice,
		VLAN_8021QDevice,
		BondDevice,
		BridgeDevice,
	}

	for _, value := range validTypes {
		result := IsValidLinkLayerDeviceType(string(value))
		c.Check(result, jc.IsTrue)
	}
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceTypeWithInvalidValue(c *gc.C) {
	result := IsValidLinkLayerDeviceType("")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceType("anything")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceType(" ")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceType("unknown")
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWithUnpatchedGOOS(c *gc.C) {
	result := IsValidLinkLayerDeviceName("valid")
	c.Check(result, jc.IsTrue)
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWithValidNamesWhenGOOSIsinux(c *gc.C) {
	s.PatchValue(&runtimeGOOS, "linux") // isolate the test from the host machine OS.

	for i, name := range validUnixDeviceNames {
		c.Logf("test #%d: %q -> valid", i, name)
		result := IsValidLinkLayerDeviceName(name)
		c.Check(result, jc.IsTrue)
	}
}

var validUnixDeviceNames = []string{
	"eth0", "eno1", "br-eth0.123", "tun:1", "bond0.42",
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWithInvalidNamesWhenGOOIsLinux(c *gc.C) {
	s.PatchValue(&runtimeGOOS, "linux") // isolate the test from the host machine OS.

	result := IsValidLinkLayerDeviceName("")
	c.Check(result, jc.IsFalse)

	const tooLongLength = 16
	result = IsValidLinkLayerDeviceName(strings.Repeat("x", tooLongLength))
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("with-hash#")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has spaces")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has\tabs")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has\newline")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has\r")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("has\vtab")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName(".")
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("..")
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWithValidNamesWhenGOOSNonLinux(c *gc.C) {
	s.PatchValue(&runtimeGOOS, "non-linux") // isolate the test from the host machine OS.
	validDeviceNames := append(validUnixDeviceNames,
		// Windows network device as friendly name and as underlying UUID.
		"Local Area Connection", "{4a62b748-43d0-4136-92e4-22ce7ee31938}",
	)

	for i, name := range validDeviceNames {
		c.Logf("test #%d: %q -> valid", i, name)
		result := IsValidLinkLayerDeviceName(name)
		c.Check(result, jc.IsTrue)
	}
}

func (s *linkLayerDevicesInternalSuite) TestIsValidLinkLayerDeviceNameWhenGOOSNonLinux(c *gc.C) {
	s.PatchValue(&runtimeGOOS, "non-linux") // isolate the test from the host machine OS.

	result := IsValidLinkLayerDeviceName("")
	c.Check(result, jc.IsFalse)

	const wayTooLongLength = 1024
	result = IsValidLinkLayerDeviceName(strings.Repeat("x", wayTooLongLength))
	c.Check(result, jc.IsFalse)

	result = IsValidLinkLayerDeviceName("hash# not allowed")
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestStringLengthBetweenWhenTooShort(c *gc.C) {
	result := stringLengthBetween("", 1, 2)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("", 1, 1)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("1", 2, 3)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("12", 3, 3)
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestStringLengthBetweenWhenTooLong(c *gc.C) {
	result := stringLengthBetween("1", 0, 0)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("12", 1, 1)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("123", 1, 2)
	c.Check(result, jc.IsFalse)

	result = stringLengthBetween("123", 0, 1)
	c.Check(result, jc.IsFalse)
}

func (s *linkLayerDevicesInternalSuite) TestStringLengthBetweenWhenWithinLimit(c *gc.C) {
	const (
		minLength = 1
		maxLength = 255
	)
	for i := minLength; i <= maxLength; i++ {
		input := strings.Repeat("x", i)
		result := stringLengthBetween(input, minLength, maxLength)
		c.Check(result, jc.IsTrue)
	}
}
<|endoftext|>


<commit_before>package builder

import (
	"fmt"
	"html/template"
	"path"

	"github.com/microcosm-cc/bluemonday"
	"github.com/russross/blackfriday"

	"github.com/aymerick/kowa/models"
	"github.com/aymerick/kowa/utils"
)

// Post nodes builder
type PostsBuilder struct {
	*NodeBuilderBase

	posts []*PostContent
}

// Post node content
type PostContent struct {
	Node  *Node
	Model *models.Post

	Date  string
	Cover *ImageVars
	Title string
	Body  template.HTML
	Url   string
}

// Posts node content
type PostsContent struct {
	Node *Node

	Posts []*PostContent
	// PrevPage string
	// NextPage string
}

func init() {
	RegisterNodeBuilder(KIND_POSTS, NewPostsBuilder)
}

// Instanciate a new builder
func NewPostsBuilder(siteBuilder *SiteBuilder) NodeBuilder {
	return &PostsBuilder{
		NodeBuilderBase: &NodeBuilderBase{
			nodeKind:    KIND_POST,
			siteBuilder: siteBuilder,
		},
	}
}

// NodeBuilder
func (builder *PostsBuilder) Load() {
	builder.loadPosts()
	builder.loadPostsLists()
}

// Build all posts
func (builder *PostsBuilder) loadPosts() {
	for _, post := range *builder.site().FindAllPosts() {
		builder.loadPost(post)
	}
}

// Computes slug
func postSlug(post *models.Post) string {
	// @todo Should use PublishedAt
	year, month, day := post.CreatedAt.Date()

	title := post.Title
	if len(title) > MAX_SLUG {
		title = title[:MAX_SLUG]
	}

	return fmt.Sprintf("%d/%02d/%02d/%s", year, month, day, utils.Urlify(title))
}

// Build post page
func (builder *PostsBuilder) loadPost(post *models.Post) {
	node := builder.newNode()
	node.fillUrl(path.Join("posts", postSlug(post))) // @todo i18n

	title := "Posts" // @todo i18n
	tagline := ""    // @todo fill

	node.Title = title
	node.Tagline = tagline
	node.Meta = &NodeMeta{
		Title:       fmt.Sprintf("%s - %s", post.Title, builder.site().Name),
		Description: tagline,
	}

	postContent := builder.NewPostContent(post, node)
	node.Content = postContent

	builder.addNode(node)

	builder.posts = append(builder.posts, postContent)
}

// Instanciate a new post content
func (builder *PostsBuilder) NewPostContent(post *models.Post, node *Node) *PostContent {
	result := &PostContent{
		Node:  node,
		Model: post,

		Date:  post.CreatedAt.Format("02/01/06"),
		Title: post.Title,
		Url:   node.Url,
	}

	cover := post.FindCover()
	if cover != nil {
		result.Cover = builder.addImage(cover)
	}

	html := blackfriday.MarkdownCommon([]byte(post.Body))
	result.Body = template.HTML(bluemonday.UGCPolicy().SanitizeBytes(html))

	return result
}

// Build posts list pages
func (builder *PostsBuilder) loadPostsLists() {
	if len(builder.posts) > 0 {
		// @todo pagination
		node := builder.newNodeForKind(KIND_POSTS)
		node.fillUrl(KIND_POSTS)

		title := "Posts" // @todo i18n
		tagline := ""    // @todo fill

		node.Title = title
		node.Tagline = tagline
		node.Meta = &NodeMeta{Description: tagline}
		node.InNavBar = true
		node.NavBarOrder = 5

		node.Content = &PostsContent{
			Node:  node,
			Posts: builder.posts,
		}

		builder.addNode(node)
	}
}
<commit_msg>builder: post body is HTML now<commit_after>package builder

import (
	"fmt"
	"html/template"
	"path"

	"github.com/microcosm-cc/bluemonday"

	"github.com/aymerick/kowa/models"
	"github.com/aymerick/kowa/utils"
)

// Post nodes builder
type PostsBuilder struct {
	*NodeBuilderBase

	posts []*PostContent
}

// Post node content
type PostContent struct {
	Node  *Node
	Model *models.Post

	Date  string
	Cover *ImageVars
	Title string
	Body  template.HTML
	Url   string
}

// Posts node content
type PostsContent struct {
	Node *Node

	Posts []*PostContent
	// PrevPage string
	// NextPage string
}

func init() {
	RegisterNodeBuilder(KIND_POSTS, NewPostsBuilder)
}

// Instanciate a new builder
func NewPostsBuilder(siteBuilder *SiteBuilder) NodeBuilder {
	return &PostsBuilder{
		NodeBuilderBase: &NodeBuilderBase{
			nodeKind:    KIND_POST,
			siteBuilder: siteBuilder,
		},
	}
}

// NodeBuilder
func (builder *PostsBuilder) Load() {
	builder.loadPosts()
	builder.loadPostsLists()
}

// Build all posts
func (builder *PostsBuilder) loadPosts() {
	for _, post := range *builder.site().FindAllPosts() {
		builder.loadPost(post)
	}
}

// Computes slug
func postSlug(post *models.Post) string {
	// @todo Should use PublishedAt
	year, month, day := post.CreatedAt.Date()

	title := post.Title
	if len(title) > MAX_SLUG {
		title = title[:MAX_SLUG]
	}

	return fmt.Sprintf("%d/%02d/%02d/%s", year, month, day, utils.Urlify(title))
}

// Build post page
func (builder *PostsBuilder) loadPost(post *models.Post) {
	node := builder.newNode()
	node.fillUrl(path.Join("posts", postSlug(post))) // @todo i18n

	title := "Posts" // @todo i18n
	tagline := ""    // @todo fill

	node.Title = title
	node.Tagline = tagline
	node.Meta = &NodeMeta{
		Title:       fmt.Sprintf("%s - %s", post.Title, builder.site().Name),
		Description: tagline,
	}

	postContent := builder.NewPostContent(post, node)
	node.Content = postContent

	builder.addNode(node)

	builder.posts = append(builder.posts, postContent)
}

// Instanciate a new post content
func (builder *PostsBuilder) NewPostContent(post *models.Post, node *Node) *PostContent {
	result := &PostContent{
		Node:  node,
		Model: post,

		Date:  post.CreatedAt.Format("02/01/06"),
		Title: post.Title,
		Url:   node.Url,
	}

	cover := post.FindCover()
	if cover != nil {
		result.Cover = builder.addImage(cover)
	}

	sanitizePolicy := bluemonday.UGCPolicy()
	sanitizePolicy.AllowAttrs("style").OnElements("p", "span") // I know this is bad

	result.Body = template.HTML(sanitizePolicy.Sanitize(post.Body))

	return result
}

// Build posts list pages
func (builder *PostsBuilder) loadPostsLists() {
	if len(builder.posts) > 0 {
		// @todo pagination
		node := builder.newNodeForKind(KIND_POSTS)
		node.fillUrl(KIND_POSTS)

		title := "Posts" // @todo i18n
		tagline := ""    // @todo fill

		node.Title = title
		node.Tagline = tagline
		node.Meta = &NodeMeta{Description: tagline}
		node.InNavBar = true
		node.NavBarOrder = 5

		node.Content = &PostsContent{
			Node:  node,
			Posts: builder.posts,
		}

		builder.addNode(node)
	}
}
<|endoftext|>


<commit_before>package controller

import (
	"fmt"
	"os"
	"sync"

	"github.com/Sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"

	"github.com/zalando-incubator/postgres-operator/pkg/apiserver"
	"github.com/zalando-incubator/postgres-operator/pkg/cluster"
	"github.com/zalando-incubator/postgres-operator/pkg/spec"
	"github.com/zalando-incubator/postgres-operator/pkg/util/config"
	"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
	"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
	"github.com/zalando-incubator/postgres-operator/pkg/util/ringlog"
)

// Controller represents operator controller
type Controller struct {
	config   spec.ControllerConfig
	opConfig *config.Config

	logger     *logrus.Entry
	KubeClient k8sutil.KubernetesClient
	apiserver  *apiserver.Server

	stopCh chan struct{}

	curWorkerID      uint32 //initialized with 0
	curWorkerCluster sync.Map
	clusterWorkers   map[spec.NamespacedName]uint32
	clustersMu       sync.RWMutex
	clusters         map[spec.NamespacedName]*cluster.Cluster
	clusterLogs      map[spec.NamespacedName]ringlog.RingLogger
	clusterHistory   map[spec.NamespacedName]ringlog.RingLogger // history of the cluster changes
	teamClusters     map[string][]spec.NamespacedName

	postgresqlInformer cache.SharedIndexInformer
	podInformer        cache.SharedIndexInformer
	nodesInformer      cache.SharedIndexInformer
	podCh              chan spec.PodEvent

	clusterEventQueues  []*cache.FIFO // [workerID]Queue
	lastClusterSyncTime int64

	workerLogs map[uint32]ringlog.RingLogger
}

// NewController creates a new controller
func NewController(controllerConfig *spec.ControllerConfig) *Controller {
	logger := logrus.New()

	c := &Controller{
		config:           *controllerConfig,
		opConfig:         &config.Config{},
		logger:           logger.WithField("pkg", "controller"),
		curWorkerCluster: sync.Map{},
		clusterWorkers:   make(map[spec.NamespacedName]uint32),
		clusters:         make(map[spec.NamespacedName]*cluster.Cluster),
		clusterLogs:      make(map[spec.NamespacedName]ringlog.RingLogger),
		clusterHistory:   make(map[spec.NamespacedName]ringlog.RingLogger),
		teamClusters:     make(map[string][]spec.NamespacedName),
		stopCh:           make(chan struct{}),
		podCh:            make(chan spec.PodEvent),
	}
	logger.Hooks.Add(c)

	return c
}

func (c *Controller) initClients() {
	var err error

	c.KubeClient, err = k8sutil.NewFromConfig(c.config.RestConfig)
	if err != nil {
		c.logger.Fatalf("could not create kubernetes clients: %v", err)
	}
}

func (c *Controller) initOperatorConfig() {
	configMapData := make(map[string]string)

	if c.config.ConfigMapName != (spec.NamespacedName{}) {
		configMap, err := c.KubeClient.ConfigMaps(c.config.ConfigMapName.Namespace).
			Get(c.config.ConfigMapName.Name, metav1.GetOptions{})
		if err != nil {
			panic(err)
		}

		configMapData = configMap.Data
	} else {
		c.logger.Infoln("no ConfigMap specified. Loading default values")
	}

	// env var takes priority over the same param from the operator ConfigMap
	watchedNamespace := os.Getenv("WATCHED_NAMESPACE")
	if watchedNamespace != "" {
		c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNamespace)
		configMapData["watched_namespace"] = watchedNamespace
	}

	if configMapData["watched_namespace"] == "" {
		c.logger.Infoln("No namespace to watch specified. Fall back to watching the 'default' namespace.")
		configMapData["watched_namespace"] = v1.NamespaceDefault
	}

	// display the actual namespace in case someone checks the value manually
	os.Setenv("WATCHED_NAMESPACE", configMapData["watched_namespace"])

	if c.config.NoDatabaseAccess {
		configMapData["enable_database_access"] = "false"
	}
	if c.config.NoTeamsAPI {
		configMapData["enable_teams_api"] = "false"
	}

	c.opConfig = config.NewFromMap(configMapData)

	scalyrAPIKey := os.Getenv("SCALYR_API_KEY")
	if scalyrAPIKey != "" {
		c.opConfig.ScalyrAPIKey = scalyrAPIKey
	}
}

func (c *Controller) initController() {
	c.initClients()
	c.initOperatorConfig()
	c.initSharedInformers()

	c.logger.Infof("config: %s", c.opConfig.MustMarshal())

	if c.opConfig.DebugLogging {
		c.logger.Logger.Level = logrus.DebugLevel
	}

	if err := c.createCRD(); err != nil {
		c.logger.Fatalf("could not register CustomResourceDefinition: %v", err)
	}

	if infraRoles, err := c.getInfrastructureRoles(&c.opConfig.InfrastructureRolesSecretName); err != nil {
		c.logger.Warningf("could not get infrastructure roles: %v", err)
	} else {
		c.config.InfrastructureRoles = infraRoles
	}

	c.clusterEventQueues = make([]*cache.FIFO, c.opConfig.Workers)
	c.workerLogs = make(map[uint32]ringlog.RingLogger, c.opConfig.Workers)
	for i := range c.clusterEventQueues {
		c.clusterEventQueues[i] = cache.NewFIFO(func(obj interface{}) (string, error) {
			e, ok := obj.(spec.ClusterEvent)
			if !ok {
				return "", fmt.Errorf("could not cast to ClusterEvent")
			}

			return queueClusterKey(e.EventType, e.UID), nil
		})
	}

	c.apiserver = apiserver.New(c, c.opConfig.APIPort, c.logger.Logger)
}

func (c *Controller) initSharedInformers() {
	// Postgresqls
	c.postgresqlInformer = cache.NewSharedIndexInformer(
		&cache.ListWatch{
			ListFunc:  c.clusterListFunc,
			WatchFunc: c.clusterWatchFunc,
		},
		&spec.Postgresql{},
		constants.QueueResyncPeriodTPR,
		cache.Indexers{})

	c.postgresqlInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.postgresqlAdd,
		UpdateFunc: c.postgresqlUpdate,
		DeleteFunc: c.postgresqlDelete,
	})

	// Pods
	podLw := &cache.ListWatch{
		ListFunc:  c.podListFunc,
		WatchFunc: c.podWatchFunc,
	}

	c.podInformer = cache.NewSharedIndexInformer(
		podLw,
		&v1.Pod{},
		constants.QueueResyncPeriodPod,
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})

	c.podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.podAdd,
		UpdateFunc: c.podUpdate,
		DeleteFunc: c.podDelete,
	})

	// Kubernetes Nodes
	nodeLw := &cache.ListWatch{
		ListFunc:  c.nodeListFunc,
		WatchFunc: c.nodeWatchFunc,
	}

	c.nodesInformer = cache.NewSharedIndexInformer(
		nodeLw,
		&v1.Node{},
		constants.QueueResyncPeriodNode,
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})

	c.nodesInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.nodeAdd,
		UpdateFunc: c.nodeUpdate,
		DeleteFunc: c.nodeDelete,
	})
}

// Run starts background controller processes
func (c *Controller) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	c.initController()

	wg.Add(5)
	go c.runPodInformer(stopCh, wg)
	go c.runPostgresqlInformer(stopCh, wg)
	go c.clusterResync(stopCh, wg)
	go c.apiserver.Run(stopCh, wg)
	go c.kubeNodesInformer(stopCh, wg)

	for i := range c.clusterEventQueues {
		wg.Add(1)
		c.workerLogs[uint32(i)] = ringlog.New(c.opConfig.RingLogLines)
		go c.processClusterEventsQueue(i, stopCh, wg)
	}

	c.logger.Info("started working in background")
}

func (c *Controller) runPodInformer(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	c.podInformer.Run(stopCh)
}

func (c *Controller) runPostgresqlInformer(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	c.postgresqlInformer.Run(stopCh)
}

func queueClusterKey(eventType spec.EventType, uid types.UID) string {
	return fmt.Sprintf("%s-%s", eventType, uid)
}

func (c *Controller) kubeNodesInformer(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	c.nodesInformer.Run(stopCh)
}
<commit_msg>Remove setting the actual watched namespace as env var (os.Setenv won't work)<commit_after>package controller

import (
	"fmt"
	"os"
	"sync"

	"github.com/Sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"

	"github.com/zalando-incubator/postgres-operator/pkg/apiserver"
	"github.com/zalando-incubator/postgres-operator/pkg/cluster"
	"github.com/zalando-incubator/postgres-operator/pkg/spec"
	"github.com/zalando-incubator/postgres-operator/pkg/util/config"
	"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
	"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
	"github.com/zalando-incubator/postgres-operator/pkg/util/ringlog"
)

// Controller represents operator controller
type Controller struct {
	config   spec.ControllerConfig
	opConfig *config.Config

	logger     *logrus.Entry
	KubeClient k8sutil.KubernetesClient
	apiserver  *apiserver.Server

	stopCh chan struct{}

	curWorkerID      uint32 //initialized with 0
	curWorkerCluster sync.Map
	clusterWorkers   map[spec.NamespacedName]uint32
	clustersMu       sync.RWMutex
	clusters         map[spec.NamespacedName]*cluster.Cluster
	clusterLogs      map[spec.NamespacedName]ringlog.RingLogger
	clusterHistory   map[spec.NamespacedName]ringlog.RingLogger // history of the cluster changes
	teamClusters     map[string][]spec.NamespacedName

	postgresqlInformer cache.SharedIndexInformer
	podInformer        cache.SharedIndexInformer
	nodesInformer      cache.SharedIndexInformer
	podCh              chan spec.PodEvent

	clusterEventQueues  []*cache.FIFO // [workerID]Queue
	lastClusterSyncTime int64

	workerLogs map[uint32]ringlog.RingLogger
}

// NewController creates a new controller
func NewController(controllerConfig *spec.ControllerConfig) *Controller {
	logger := logrus.New()

	c := &Controller{
		config:           *controllerConfig,
		opConfig:         &config.Config{},
		logger:           logger.WithField("pkg", "controller"),
		curWorkerCluster: sync.Map{},
		clusterWorkers:   make(map[spec.NamespacedName]uint32),
		clusters:         make(map[spec.NamespacedName]*cluster.Cluster),
		clusterLogs:      make(map[spec.NamespacedName]ringlog.RingLogger),
		clusterHistory:   make(map[spec.NamespacedName]ringlog.RingLogger),
		teamClusters:     make(map[string][]spec.NamespacedName),
		stopCh:           make(chan struct{}),
		podCh:            make(chan spec.PodEvent),
	}
	logger.Hooks.Add(c)

	return c
}

func (c *Controller) initClients() {
	var err error

	c.KubeClient, err = k8sutil.NewFromConfig(c.config.RestConfig)
	if err != nil {
		c.logger.Fatalf("could not create kubernetes clients: %v", err)
	}
}

func (c *Controller) initOperatorConfig() {
	configMapData := make(map[string]string)

	if c.config.ConfigMapName != (spec.NamespacedName{}) {
		configMap, err := c.KubeClient.ConfigMaps(c.config.ConfigMapName.Namespace).
			Get(c.config.ConfigMapName.Name, metav1.GetOptions{})
		if err != nil {
			panic(err)
		}

		configMapData = configMap.Data
	} else {
		c.logger.Infoln("no ConfigMap specified. Loading default values")
	}

	// env var takes priority over the same param from the operator ConfigMap
	watchedNamespace := os.Getenv("WATCHED_NAMESPACE")
	if watchedNamespace != "" {
		c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNamespace)
		configMapData["watched_namespace"] = watchedNamespace
	}

	if configMapData["watched_namespace"] == "" {
		c.logger.Infoln("No namespace to watch specified. Fall back to watching the 'default' namespace.")
		configMapData["watched_namespace"] = v1.NamespaceDefault
	}

	if c.config.NoDatabaseAccess {
		configMapData["enable_database_access"] = "false"
	}
	if c.config.NoTeamsAPI {
		configMapData["enable_teams_api"] = "false"
	}

	c.opConfig = config.NewFromMap(configMapData)

	scalyrAPIKey := os.Getenv("SCALYR_API_KEY")
	if scalyrAPIKey != "" {
		c.opConfig.ScalyrAPIKey = scalyrAPIKey
	}
}

func (c *Controller) initController() {
	c.initClients()
	c.initOperatorConfig()
	c.initSharedInformers()

	c.logger.Infof("config: %s", c.opConfig.MustMarshal())

	if c.opConfig.DebugLogging {
		c.logger.Logger.Level = logrus.DebugLevel
	}

	if err := c.createCRD(); err != nil {
		c.logger.Fatalf("could not register CustomResourceDefinition: %v", err)
	}

	if infraRoles, err := c.getInfrastructureRoles(&c.opConfig.InfrastructureRolesSecretName); err != nil {
		c.logger.Warningf("could not get infrastructure roles: %v", err)
	} else {
		c.config.InfrastructureRoles = infraRoles
	}

	c.clusterEventQueues = make([]*cache.FIFO, c.opConfig.Workers)
	c.workerLogs = make(map[uint32]ringlog.RingLogger, c.opConfig.Workers)
	for i := range c.clusterEventQueues {
		c.clusterEventQueues[i] = cache.NewFIFO(func(obj interface{}) (string, error) {
			e, ok := obj.(spec.ClusterEvent)
			if !ok {
				return "", fmt.Errorf("could not cast to ClusterEvent")
			}

			return queueClusterKey(e.EventType, e.UID), nil
		})
	}

	c.apiserver = apiserver.New(c, c.opConfig.APIPort, c.logger.Logger)
}

func (c *Controller) initSharedInformers() {
	// Postgresqls
	c.postgresqlInformer = cache.NewSharedIndexInformer(
		&cache.ListWatch{
			ListFunc:  c.clusterListFunc,
			WatchFunc: c.clusterWatchFunc,
		},
		&spec.Postgresql{},
		constants.QueueResyncPeriodTPR,
		cache.Indexers{})

	c.postgresqlInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.postgresqlAdd,
		UpdateFunc: c.postgresqlUpdate,
		DeleteFunc: c.postgresqlDelete,
	})

	// Pods
	podLw := &cache.ListWatch{
		ListFunc:  c.podListFunc,
		WatchFunc: c.podWatchFunc,
	}

	c.podInformer = cache.NewSharedIndexInformer(
		podLw,
		&v1.Pod{},
		constants.QueueResyncPeriodPod,
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})

	c.podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.podAdd,
		UpdateFunc: c.podUpdate,
		DeleteFunc: c.podDelete,
	})

	// Kubernetes Nodes
	nodeLw := &cache.ListWatch{
		ListFunc:  c.nodeListFunc,
		WatchFunc: c.nodeWatchFunc,
	}

	c.nodesInformer = cache.NewSharedIndexInformer(
		nodeLw,
		&v1.Node{},
		constants.QueueResyncPeriodNode,
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})

	c.nodesInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.nodeAdd,
		UpdateFunc: c.nodeUpdate,
		DeleteFunc: c.nodeDelete,
	})
}

// Run starts background controller processes
func (c *Controller) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	c.initController()

	wg.Add(5)
	go c.runPodInformer(stopCh, wg)
	go c.runPostgresqlInformer(stopCh, wg)
	go c.clusterResync(stopCh, wg)
	go c.apiserver.Run(stopCh, wg)
	go c.kubeNodesInformer(stopCh, wg)

	for i := range c.clusterEventQueues {
		wg.Add(1)
		c.workerLogs[uint32(i)] = ringlog.New(c.opConfig.RingLogLines)
		go c.processClusterEventsQueue(i, stopCh, wg)
	}

	c.logger.Info("started working in background")
}

func (c *Controller) runPodInformer(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	c.podInformer.Run(stopCh)
}

func (c *Controller) runPostgresqlInformer(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	c.postgresqlInformer.Run(stopCh)
}

func queueClusterKey(eventType spec.EventType, uid types.UID) string {
	return fmt.Sprintf("%s-%s", eventType, uid)
}

func (c *Controller) kubeNodesInformer(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	c.nodesInformer.Run(stopCh)
}
<|endoftext|>


<commit_before>// Copyright 2020 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package ifuzz allows to generate and mutate PPC64 PowerISA 3.0B machine code.

// The ISA for POWER9 (the latest available at the moment) is at:
// https://openpowerfoundation.org/?resource_lib=power-isa-version-3-0
//
// A script on top of pdftotext was used to produce insns.go:
// ./powerisa30_to_syz /home/aik/Documents/ppc/power9/PowerISA_public.v3.0B.pdf > 1.go
// .

package powerpc

import (
	"encoding/binary"
	"errors"
	"fmt"
	"math/rand"

	"github.com/google/syzkaller/pkg/ifuzz/iset"
)

type InsnBits struct {
	Start  uint // Big endian bit order.
	Length uint
}

type Insn struct {
	Name   string
	M64    bool // true if the instruction is 64bit _only_.
	Priv   bool
	Pseudo bool
	Fields map[string]InsnBits // for ra/rb/rt/si/...
	Opcode uint32
	Mask   uint32

	insnMap   *insnSetMap
	generator func(cfg *iset.Config, r *rand.Rand) []byte
}

type insnSetMap map[string]*Insn

type InsnSet struct {
	Insns     []*Insn
	modeInsns iset.ModeInsns
	insnMap   insnSetMap
}

func (insnset *InsnSet) GetInsns(mode iset.Mode, typ iset.Type) []iset.Insn {
	return insnset.modeInsns[mode][typ]
}

func (insnset *InsnSet) Decode(mode iset.Mode, text []byte) (int, error) {
	if len(text) < 4 {
		return 0, errors.New("must be at least 4 bytes")
	}
	insn32 := binary.LittleEndian.Uint32(text)
	for _, ins := range insnset.Insns {
		if ins.Mask&insn32 == ins.Opcode {
			return 4, nil
		}
	}
	return 0, fmt.Errorf("unrecognised instruction %08x", insn32)
}

func (insnset *InsnSet) DecodeExt(mode iset.Mode, text []byte) (int, error) {
	return 0, fmt.Errorf("no external decoder")
}

func encodeBits(n uint, f InsnBits) uint32 {
	mask := uint(1<<f.Length) - 1
	return uint32((n & mask) << (31 - (f.Start + f.Length - 1)))
}

func (insn Insn) Encode(cfg *iset.Config, r *rand.Rand) []byte {
	if insn.Pseudo {
		return insn.generator(cfg, r)
	}

	ret := make([]byte, 0)
	insn32 := insn.Opcode
	for reg, bits := range insn.Fields {
		field := uint(r.Intn(1 << 16))
		insn32 |= encodeBits(field, bits)
		if len(cfg.MemRegions) != 0 && (reg == "RA" || reg == "RB") {
			val := iset.GenerateInt(cfg, r, 8)
			ret = append(ret, insn.insnMap.ld64(field, val)...)
		}
	}

	return append(ret, uint32toBytes(insn32)...)
}

func Register(insns []*Insn) {
	if len(insns) == 0 {
		panic("no instructions")
	}
	insnset := &InsnSet{
		Insns:   insns,
		insnMap: make(map[string]*Insn),
	}
	for _, insn := range insnset.Insns {
		insnset.insnMap[insn.Name] = insn
		insn.insnMap = &insnset.insnMap
	}
	insnset.initPseudo()
	for _, insn := range insnset.Insns {
		insnset.modeInsns.Add(insn)
	}
	iset.Arches[iset.ArchPowerPC] = insnset
}

func (insn *Insn) Info() (string, iset.Mode, bool, bool) {
	return insn.Name, insn.mode(), insn.Pseudo, insn.Priv
}

func (insn Insn) mode() iset.Mode {
	if insn.M64 {
		return (1 << iset.ModeLong64)
	}
	return (1 << iset.ModeLong64) | (1 << iset.ModeProt32)
}

func uint32toBytes(v uint32) []byte {
	ret := make([]byte, 4)
	binary.LittleEndian.PutUint32(ret, v)

	return ret
}

func (insn *Insn) enc(v map[string]uint) []byte {
	insn32 := insn.Opcode
	for reg, bits := range insn.Fields {
		if val, ok := v[reg]; ok {
			insn32 |= encodeBits(val, bits)
		}
	}
	return uint32toBytes(insn32)
}

func (imap insnSetMap) ld64(reg uint, v uint64) []byte {
	ret := make([]byte, 0)

	// This is a widely used macro to load immediate on ppc64
	// #define LOAD64(rn,name)
	//	addis   rn,0,name##@highest \ lis     rn,name##@highest
	//	ori     rn,rn,name##@higher
	//	rldicr  rn,rn,32,31
	//	oris    rn,rn,name##@h
	//	ori     rn,rn,name##@l
	ret = append(ret, imap["addis"].enc(map[string]uint{
		"RT": reg,
		"RA": 0, // In "addis", '0' means 0, not GPR0 .
		"SI": uint((v >> 48) & 0xffff)})...)
	ret = append(ret, imap["ori"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"UI": uint((v >> 32) & 0xffff)})...)
	ret = append(ret, imap["rldicr"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"SH": 32,
		"ME": 31})...)
	ret = append(ret, imap["oris"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"UI": uint((v >> 16) & 0xffff)})...)
	ret = append(ret, imap["ori"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"UI": uint(v & 0xffff)})...)

	return ret
}

func (imap insnSetMap) ld32(reg uint, v uint32) []byte {
	ret := make([]byte, 0)

	ret = append(ret, imap["addis"].enc(map[string]uint{
		"RT": reg,
		"RA": 0, // In "addis", '0' means 0, not GPR0
		"SI": uint((v >> 16) & 0xffff)})...)
	ret = append(ret, imap["ori"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"UI": uint(v & 0xffff)})...)

	return ret
}

func (imap insnSetMap) ldgpr32(regaddr, regval uint, addr uint64, v uint32) []byte {
	ret := make([]byte, 0)

	ret = append(ret, imap.ld64(regaddr, addr)...)
	ret = append(ret, imap.ld32(regval, v)...)
	ret = append(ret, imap["stw"].enc(map[string]uint{
		"RA": regaddr,
		"RS": regval})...)

	return ret
}

func (imap insnSetMap) sc(lev uint) []byte {
	return imap["sc"].enc(map[string]uint{"LEV": lev})
}
<commit_msg>pkg/ifuzz/powerpc: fuzz instruction bits which are not opcodes<commit_after>// Copyright 2020 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package ifuzz allows to generate and mutate PPC64 PowerISA 3.0B machine code.

// The ISA for POWER9 (the latest available at the moment) is at:
// https://openpowerfoundation.org/?resource_lib=power-isa-version-3-0
//
// A script on top of pdftotext was used to produce insns.go:
// ./powerisa30_to_syz /home/aik/Documents/ppc/power9/PowerISA_public.v3.0B.pdf > 1.go
// .

package powerpc

import (
	"encoding/binary"
	"errors"
	"fmt"
	"math/rand"

	"github.com/google/syzkaller/pkg/ifuzz/iset"
)

type InsnBits struct {
	Start  uint // Big endian bit order.
	Length uint
}

type Insn struct {
	Name   string
	M64    bool // true if the instruction is 64bit _only_.
	Priv   bool
	Pseudo bool
	Fields map[string]InsnBits // for ra/rb/rt/si/...
	Opcode uint32
	Mask   uint32

	insnMap   *insnSetMap
	generator func(cfg *iset.Config, r *rand.Rand) []byte
}

type insnSetMap map[string]*Insn

type InsnSet struct {
	Insns     []*Insn
	modeInsns iset.ModeInsns
	insnMap   insnSetMap
}

func (insnset *InsnSet) GetInsns(mode iset.Mode, typ iset.Type) []iset.Insn {
	return insnset.modeInsns[mode][typ]
}

func (insnset *InsnSet) Decode(mode iset.Mode, text []byte) (int, error) {
	if len(text) < 4 {
		return 0, errors.New("must be at least 4 bytes")
	}
	insn32 := binary.LittleEndian.Uint32(text)
	for _, ins := range insnset.Insns {
		if ins.Mask&insn32 == ins.Opcode {
			return 4, nil
		}
	}
	return 0, fmt.Errorf("unrecognised instruction %08x", insn32)
}

func (insnset *InsnSet) DecodeExt(mode iset.Mode, text []byte) (int, error) {
	return 0, fmt.Errorf("no external decoder")
}

func encodeBits(n uint, f InsnBits) uint32 {
	mask := uint(1<<f.Length) - 1
	return uint32((n & mask) << (31 - (f.Start + f.Length - 1)))
}

func (insn Insn) Encode(cfg *iset.Config, r *rand.Rand) []byte {
	if insn.Pseudo {
		return insn.generator(cfg, r)
	}

	ret := make([]byte, 0)
	insn32 := insn.Opcode
	if len(cfg.MemRegions) != 0 {
		// The PowerISA pdf parser could have missed some fields,
		// randomize them there.
		insn32 |= r.Uint32() & ^insn.Mask
	}
	for reg, bits := range insn.Fields {
		field := uint(r.Intn(1 << 16))
		insn32 |= encodeBits(field, bits)
		if len(cfg.MemRegions) != 0 && (reg == "RA" || reg == "RB" || reg == "RS") {
			val := iset.GenerateInt(cfg, r, 8)
			ret = append(ret, insn.insnMap.ld64(field, val)...)
		}
	}

	return append(ret, uint32toBytes(insn32)...)
}

func Register(insns []*Insn) {
	if len(insns) == 0 {
		panic("no instructions")
	}
	insnset := &InsnSet{
		Insns:   insns,
		insnMap: make(map[string]*Insn),
	}
	for _, insn := range insnset.Insns {
		insnset.insnMap[insn.Name] = insn
		insn.insnMap = &insnset.insnMap
	}
	insnset.initPseudo()
	for _, insn := range insnset.Insns {
		insnset.modeInsns.Add(insn)
	}
	iset.Arches[iset.ArchPowerPC] = insnset
}

func (insn *Insn) Info() (string, iset.Mode, bool, bool) {
	return insn.Name, insn.mode(), insn.Pseudo, insn.Priv
}

func (insn Insn) mode() iset.Mode {
	if insn.M64 {
		return (1 << iset.ModeLong64)
	}
	return (1 << iset.ModeLong64) | (1 << iset.ModeProt32)
}

func uint32toBytes(v uint32) []byte {
	ret := make([]byte, 4)
	binary.LittleEndian.PutUint32(ret, v)

	return ret
}

func (insn *Insn) enc(v map[string]uint) []byte {
	insn32 := insn.Opcode
	for reg, bits := range insn.Fields {
		if val, ok := v[reg]; ok {
			insn32 |= encodeBits(val, bits)
		}
	}
	return uint32toBytes(insn32)
}

func (imap insnSetMap) ld64(reg uint, v uint64) []byte {
	ret := make([]byte, 0)

	// This is a widely used macro to load immediate on ppc64
	// #define LOAD64(rn,name)
	//	addis   rn,0,name##@highest \ lis     rn,name##@highest
	//	ori     rn,rn,name##@higher
	//	rldicr  rn,rn,32,31
	//	oris    rn,rn,name##@h
	//	ori     rn,rn,name##@l
	ret = append(ret, imap["addis"].enc(map[string]uint{
		"RT": reg,
		"RA": 0, // In "addis", '0' means 0, not GPR0 .
		"SI": uint((v >> 48) & 0xffff)})...)
	ret = append(ret, imap["ori"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"UI": uint((v >> 32) & 0xffff)})...)
	ret = append(ret, imap["rldicr"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"SH": 32,
		"ME": 31})...)
	ret = append(ret, imap["oris"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"UI": uint((v >> 16) & 0xffff)})...)
	ret = append(ret, imap["ori"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"UI": uint(v & 0xffff)})...)

	return ret
}

func (imap insnSetMap) ld32(reg uint, v uint32) []byte {
	ret := make([]byte, 0)

	ret = append(ret, imap["addis"].enc(map[string]uint{
		"RT": reg,
		"RA": 0, // In "addis", '0' means 0, not GPR0
		"SI": uint((v >> 16) & 0xffff)})...)
	ret = append(ret, imap["ori"].enc(map[string]uint{
		"RA": reg,
		"RS": reg,
		"UI": uint(v & 0xffff)})...)

	return ret
}

func (imap insnSetMap) ldgpr32(regaddr, regval uint, addr uint64, v uint32) []byte {
	ret := make([]byte, 0)

	ret = append(ret, imap.ld64(regaddr, addr)...)
	ret = append(ret, imap.ld32(regval, v)...)
	ret = append(ret, imap["stw"].enc(map[string]uint{
		"RA": regaddr,
		"RS": regval})...)

	return ret
}

func (imap insnSetMap) sc(lev uint) []byte {
	return imap["sc"].enc(map[string]uint{"LEV": lev})
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative 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 prober

import (
	"context"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"github.com/pkg/errors"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/knative/serving/pkg/network"
)

// TransportFactory is a function which returns a HTTP transport
type TransportFactory func() http.RoundTripper

// ProbeOption is a way for caller to modify the HTTP request before it goes out.
type ProbeOption func(r *http.Request) *http.Request

// Do sends a single probe to given target, e.g. `http://revision.default.svc.cluster.local:81`.
// headerValue is the value for the `k-network-probe` header.
// Do returns whether the probe was successful or not, or there was an error probing.
func Do(ctx context.Context, transport http.RoundTripper, target, headerValue string, pos ...ProbeOption) (bool, error) {
	req, err := http.NewRequest(http.MethodGet, target, nil)
	if err != nil {
		return false, errors.Wrapf(err, "%s is not a valid URL", target)
	}
	for _, po := range pos {
		req = po(req)
	}

	req.Header.Set(network.ProbeHeaderName, headerValue)
	req = req.WithContext(ctx)
	resp, err := transport.RoundTrip(req)
	if err != nil {
		return false, errors.Wrapf(err, "error roundtripping %s", target)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return false, errors.Wrap(err, "error reading body")
	}
	return resp.StatusCode == http.StatusOK && string(body) == headerValue, nil
}

// Done is a callback that is executed when the async probe has finished.
// `arg` is given by the caller at the offering time, while `success` and `err`
// are the return values of the `Do` call.
// It is assumed that the opaque arg is consistent for a given target and
// we will coalesce concurrent Offer invocations on target.
type Done func(arg interface{}, success bool, err error)

// Manager manages async probes and makes sure we run concurrently only a single
// probe for the same key.
type Manager struct {
	cb               Done
	transportFactory TransportFactory

	// mu guards keys.
	mu   sync.Mutex
	keys sets.String
}

// New creates a new Manager, that will invoke the given callback when
// async probing is finished.
func New(cb Done, transportFactory TransportFactory) *Manager {
	return &Manager{
		keys:             sets.NewString(),
		cb:               cb,
		transportFactory: transportFactory,
	}
}

// Offer executes asynchronous probe using `target` as the key.
// If a probe with the same key already exists, Offer will return false and the
// call is discarded. If the request is accepted, Offer returns true.
// Otherwise Offer starts a goroutine that periodically executes
// `Do`, until timeout is reached, the probe succeeds, or fails with an error.
// In the end the callback is invoked with the provided `arg` and probing results.
func (m *Manager) Offer(ctx context.Context, target, headerValue string, arg interface{}, period, timeout time.Duration) bool {
	m.mu.Lock()
	defer m.mu.Unlock()
	if m.keys.Has(target) {
		return false
	}
	m.keys.Insert(target)
	m.doAsync(ctx, m.transportFactory, target, headerValue, arg, period, timeout)
	return true
}

// doAsync starts a go routine that probes the target with given period.
func (m *Manager) doAsync(ctx context.Context, transportFactory TransportFactory, target, headerValue string, arg interface{}, period, timeout time.Duration) {
	go func() {
		defer func() {
			m.mu.Lock()
			defer m.mu.Unlock()
			m.keys.Delete(target)
		}()
		var (
			result bool
			err    error
		)
		err = wait.PollImmediate(period, timeout, func() (bool, error) {
			result, err = Do(ctx, transportFactory(), target, headerValue)
			return result, err
		})
		m.cb(arg, result, err)
	}()
}
<commit_msg>Make sure we clearly state why the factory is required there (#4509)<commit_after>/*
Copyright 2019 The Knative 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 prober

import (
	"context"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"github.com/pkg/errors"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/knative/serving/pkg/network"
)

// TransportFactory is a function which returns an HTTP transport.
type TransportFactory func() http.RoundTripper

// ProbeOption is a way for caller to modify the HTTP request before it goes out.
type ProbeOption func(r *http.Request) *http.Request

// Do sends a single probe to given target, e.g. `http://revision.default.svc.cluster.local:81`.
// headerValue is the value for the `k-network-probe` header.
// Do returns whether the probe was successful or not, or there was an error probing.
func Do(ctx context.Context, transport http.RoundTripper, target, headerValue string, pos ...ProbeOption) (bool, error) {
	req, err := http.NewRequest(http.MethodGet, target, nil)
	if err != nil {
		return false, errors.Wrapf(err, "%s is not a valid URL", target)
	}
	for _, po := range pos {
		req = po(req)
	}

	req.Header.Set(network.ProbeHeaderName, headerValue)
	req = req.WithContext(ctx)
	resp, err := transport.RoundTrip(req)
	if err != nil {
		return false, errors.Wrapf(err, "error roundtripping %s", target)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return false, errors.Wrap(err, "error reading body")
	}
	return resp.StatusCode == http.StatusOK && string(body) == headerValue, nil
}

// Done is a callback that is executed when the async probe has finished.
// `arg` is given by the caller at the offering time, while `success` and `err`
// are the return values of the `Do` call.
// It is assumed that the opaque arg is consistent for a given target and
// we will coalesce concurrent Offer invocations on target.
type Done func(arg interface{}, success bool, err error)

// Manager manages async probes and makes sure we run concurrently only a single
// probe for the same key.
type Manager struct {
	cb Done
	// NB: it is paramount to use factory here, since we need a fresh roundtripper
	// for every request. The way K8s Services work is that they won't terminate
	// the TCP connection while the backend is still alive, and we won't scale it
	// to zero, until we receive an activator response. Without mesh the connection
	// is never severed and we continue probing the pod.
	transportFactory TransportFactory

	// mu guards keys.
	mu   sync.Mutex
	keys sets.String
}

// New creates a new Manager, that will invoke the given callback when
// async probing is finished.
func New(cb Done, transportFactory TransportFactory) *Manager {
	return &Manager{
		keys:             sets.NewString(),
		cb:               cb,
		transportFactory: transportFactory,
	}
}

// Offer executes asynchronous probe using `target` as the key.
// If a probe with the same key already exists, Offer will return false and the
// call is discarded. If the request is accepted, Offer returns true.
// Otherwise Offer starts a goroutine that periodically executes
// `Do`, until timeout is reached, the probe succeeds, or fails with an error.
// In the end the callback is invoked with the provided `arg` and probing results.
func (m *Manager) Offer(ctx context.Context, target, headerValue string, arg interface{}, period, timeout time.Duration) bool {
	m.mu.Lock()
	defer m.mu.Unlock()
	if m.keys.Has(target) {
		return false
	}
	m.keys.Insert(target)
	m.doAsync(ctx, m.transportFactory, target, headerValue, arg, period, timeout)
	return true
}

// doAsync starts a go routine that probes the target with given period.
func (m *Manager) doAsync(ctx context.Context, transportFactory TransportFactory, target, headerValue string, arg interface{}, period, timeout time.Duration) {
	go func() {
		defer func() {
			m.mu.Lock()
			defer m.mu.Unlock()
			m.keys.Delete(target)
		}()
		var (
			result bool
			err    error
		)
		err = wait.PollImmediate(period, timeout, func() (bool, error) {
			result, err = Do(ctx, transportFactory(), target, headerValue)
			return result, err
		})
		m.cb(arg, result, err)
	}()
}
<|endoftext|>