<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package main

import (
	"errors"
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"k8s.io/component-base/logs"
	"k8s.io/klog/v2"
)

func main() {
	command := NewLoggerCommand()
	logs.InitLogs()
	defer logs.FlushLogs()

	if err := command.Execute(); err != nil {
		os.Exit(1)
	}
}

func NewLoggerCommand() *cobra.Command {
	o := logs.NewOptions()
	cmd := &cobra.Command{
		Run: func(cmd *cobra.Command, args []string) {
			errs := o.Validate()
			if len(errs) != 0 {
				fmt.Fprintf(os.Stderr, "%v\n", errs)
				os.Exit(1)
			}
			o.Apply()
			runLogger()
		},
	}
	o.AddFlags(cmd.Flags())
	return cmd
}

func runLogger() {
	klog.Infof("Log using Infof, key: %s", "value")
	klog.InfoS("Log using InfoS", "key", "value")
	err := errors.New("fail")
	klog.Errorf("Log using Errorf, err: %v", err)
	klog.ErrorS(err, "Log using ErrorS")
	data := SensitiveData{Key: "secret"}
	klog.Infof("Log with sensitive key, data: %q", data)
}

type SensitiveData struct {
	Key string `json:"key" datapolicy:"secret-key"`
}
<commit_msg>component-base: enable JSON in example<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package main

import (
	"errors"
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"k8s.io/component-base/logs"
	"k8s.io/klog/v2"

	_ "k8s.io/component-base/logs/json/register"
)

func main() {
	command := NewLoggerCommand()
	logs.InitLogs()
	defer logs.FlushLogs()

	if err := command.Execute(); err != nil {
		os.Exit(1)
	}
}

func NewLoggerCommand() *cobra.Command {
	o := logs.NewOptions()
	cmd := &cobra.Command{
		Run: func(cmd *cobra.Command, args []string) {
			errs := o.Validate()
			if len(errs) != 0 {
				fmt.Fprintf(os.Stderr, "%v\n", errs)
				os.Exit(1)
			}
			o.Apply()
			runLogger()
		},
	}
	o.AddFlags(cmd.Flags())
	return cmd
}

func runLogger() {
	klog.Infof("Log using Infof, key: %s", "value")
	klog.InfoS("Log using InfoS", "key", "value")
	err := errors.New("fail")
	klog.Errorf("Log using Errorf, err: %v", err)
	klog.ErrorS(err, "Log using ErrorS")
	data := SensitiveData{Key: "secret"}
	klog.Infof("Log with sensitive key, data: %q", data)
}

type SensitiveData struct {
	Key string `json:"key" datapolicy:"secret-key"`
}
<|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/emicklei/go-restful/swagger"
	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/errors"
	errs "k8s.io/kubernetes/pkg/util/fielderrors"
	"k8s.io/kubernetes/pkg/util/yaml"
)

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"]
	if apiVersion == nil {
		return fmt.Errorf("apiVersion not set")
	}
	kind := fields["kind"]
	if kind == nil {
		return fmt.Errorf("kind not set")
	}
	allErrs := s.ValidateObject(obj, apiVersion.(string), "", apiVersion.(string)+"."+kind.(string))
	if len(allErrs) == 1 {
		return allErrs[0]
	}
	return errors.NewAggregate(allErrs)
}

func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, typeName string) errs.ValidationErrorList {
	allErrs := errs.ValidationErrorList{}
	models := s.api.Models
	// TODO: handle required fields here too.
	model, ok := models.At(typeName)
	if !ok {
		return append(allErrs, 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 append(allErrs, fmt.Errorf("field %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, obj))
	}
	if len(fieldName) > 0 {
		fieldName = fieldName + "."
	}
	// handle required fields
	for _, requiredKey := range model.Required {
		if _, ok := fields[requiredKey]; !ok {
			allErrs = append(allErrs, fmt.Errorf("field %s: is required", requiredKey))
		}
	}
	for key, value := range fields {
		details, ok := properties.At(key)
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("found invalid field %s for %s", key, typeName))
			continue
		}
		if details.Type == nil && details.Ref == nil {
			allErrs = append(allErrs, 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
		}
		errs := s.validateField(value, apiVersion, fieldName+key, fieldType, &details)
		if len(errs) > 0 {
			allErrs = append(allErrs, errs...)
		}
	}
	return allErrs
}

func (s *SwaggerSchema) validateField(value interface{}, apiVersion, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) errs.ValidationErrorList {
	if strings.HasPrefix(fieldType, apiVersion) {
		return s.ValidateObject(value, apiVersion, fieldName, fieldType)
	}
	allErrs := errs.ValidationErrorList{}
	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 append(allErrs, NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "array":
		arr, ok := value.([]interface{})
		if !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		var arrType string
		if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil {
			return append(allErrs, 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 {
			errs := s.validateField(arr[ix], apiVersion, fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil)
			if len(errs) > 0 {
				allErrs = append(allErrs, errs...)
			}
		}
	case "uint64":
	case "int64":
	case "integer":
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "float64":
		if _, ok := value.(float64); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "boolean":
		if _, ok := value.(bool); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "any":
	default:
		return append(allErrs, fmt.Errorf("unexpected type: %v", fieldType))
	}
	return allErrs
}
<commit_msg>Remove useless todo notes that handle required fields<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/emicklei/go-restful/swagger"
	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/errors"
	errs "k8s.io/kubernetes/pkg/util/fielderrors"
	"k8s.io/kubernetes/pkg/util/yaml"
)

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"]
	if apiVersion == nil {
		return fmt.Errorf("apiVersion not set")
	}
	kind := fields["kind"]
	if kind == nil {
		return fmt.Errorf("kind not set")
	}
	allErrs := s.ValidateObject(obj, apiVersion.(string), "", apiVersion.(string)+"."+kind.(string))
	if len(allErrs) == 1 {
		return allErrs[0]
	}
	return errors.NewAggregate(allErrs)
}

func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, typeName string) errs.ValidationErrorList {
	allErrs := errs.ValidationErrorList{}
	models := s.api.Models
	model, ok := models.At(typeName)
	if !ok {
		return append(allErrs, 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 append(allErrs, fmt.Errorf("field %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, obj))
	}
	if len(fieldName) > 0 {
		fieldName = fieldName + "."
	}
	// handle required fields
	for _, requiredKey := range model.Required {
		if _, ok := fields[requiredKey]; !ok {
			allErrs = append(allErrs, fmt.Errorf("field %s: is required", requiredKey))
		}
	}
	for key, value := range fields {
		details, ok := properties.At(key)
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("found invalid field %s for %s", key, typeName))
			continue
		}
		if details.Type == nil && details.Ref == nil {
			allErrs = append(allErrs, 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
		}
		errs := s.validateField(value, apiVersion, fieldName+key, fieldType, &details)
		if len(errs) > 0 {
			allErrs = append(allErrs, errs...)
		}
	}
	return allErrs
}

func (s *SwaggerSchema) validateField(value interface{}, apiVersion, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) errs.ValidationErrorList {
	if strings.HasPrefix(fieldType, apiVersion) {
		return s.ValidateObject(value, apiVersion, fieldName, fieldType)
	}
	allErrs := errs.ValidationErrorList{}
	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 append(allErrs, NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "array":
		arr, ok := value.([]interface{})
		if !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		var arrType string
		if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil {
			return append(allErrs, 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 {
			errs := s.validateField(arr[ix], apiVersion, fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil)
			if len(errs) > 0 {
				allErrs = append(allErrs, errs...)
			}
		}
	case "uint64":
	case "int64":
	case "integer":
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "float64":
		if _, ok := value.(float64); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "boolean":
		if _, ok := value.(bool); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "any":
	default:
		return append(allErrs, fmt.Errorf("unexpected type: %v", fieldType))
	}
	return allErrs
}
<|endoftext|>


<commit_before>package buildlog

import (
	"fmt"
	"log"

	"github.com/mattes/migrate"
	"github.com/mattes/migrate/database/postgres"
	_ "github.com/mattes/migrate/source/file"
)

type migrationLogger struct {
}

func (m migrationLogger) Verbose() bool {
	return false
}

func (m migrationLogger) Printf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	log.Printf("[db migration] %s", s)
}

func (bl *BuildLog) MigrateDb() error {
	driver, err := postgres.WithInstance(bl.db, &postgres.Config{})
	if err != nil {
		return err
	}

	m, err := migrate.NewWithDatabaseInstance("file://migrations", "postgres", driver)
	if err != nil {
		return err
	}

	m.Log = migrationLogger{}

	err = m.Up()
	if err != migrate.ErrNoChange {
		return err
	}

	return nil
}
<commit_msg>Allow DB migrations source path customization<commit_after>package buildlog

import (
	"fmt"
	"log"
	"os"

	"github.com/mattes/migrate"
	"github.com/mattes/migrate/database/postgres"
	_ "github.com/mattes/migrate/source/file"
)

type migrationLogger struct {
}

func (m migrationLogger) Verbose() bool {
	return false
}

func (m migrationLogger) Printf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	log.Printf("[db migration] %s", s)
}

func (bl *BuildLog) MigrateDb() error {
	driver, err := postgres.WithInstance(bl.db, &postgres.Config{})
	if err != nil {
		return err
	}

	source := os.Getenv("DB_MIGRATIONS_SOURCE_URI")
	if source == "" {
		source = "file://migrations"
	}
	log.Printf("Using DB migrations file from %s\n", source)

	m, err := migrate.NewWithDatabaseInstance(source, "postgres", driver)
	if err != nil {
		return err
	}

	m.Log = migrationLogger{}

	err = m.Up()
	if err != migrate.ErrNoChange {
		return err
	}

	return nil
}
<|endoftext|>


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

package controller

import (
	"context"
	"fmt"
	"runtime/debug"
	"strings"
	"time"

	k8sv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/log"
	cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1beta1"
)

const (
	// BurstReplicas is the maximum amount of requests in a row for CRUD operations on resources by controllers,
	// to avoid unintentional DoS
	BurstReplicas uint = 250
)

// NewListWatchFromClient creates a new ListWatch from the specified client, resource, kubevirtNamespace and field selector.
func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch {
	listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
		options.FieldSelector = fieldSelector.String()
		options.LabelSelector = labelSelector.String()
		return c.Get().
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, metav1.ParameterCodec).
			Do(context.Background()).
			Get()
	}
	watchFunc := func(options metav1.ListOptions) (watch.Interface, error) {
		options.FieldSelector = fieldSelector.String()
		options.LabelSelector = labelSelector.String()
		return c.Get().
			Prefix("watch").
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, metav1.ParameterCodec).
			Watch(context.Background())
	}
	return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
}

func HandlePanic() {
	if r := recover(); r != nil {
		log.Log.Level(log.FATAL).Log("stacktrace", debug.Stack(), "msg", r)
	}
}

func NewResourceEventHandlerFuncsForWorkqueue(queue workqueue.RateLimitingInterface) cache.ResourceEventHandlerFuncs {
	return cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := KeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
		UpdateFunc: func(old interface{}, new interface{}) {
			key, err := KeyFunc(new)
			if err == nil {
				queue.Add(key)
			}
		},
		DeleteFunc: func(obj interface{}) {
			key, err := KeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
	}
}

func MigrationKey(migration *v1.VirtualMachineInstanceMigration) string {
	return fmt.Sprintf("%v/%v", migration.ObjectMeta.Namespace, migration.ObjectMeta.Name)
}

func VirtualMachineInstanceKey(vmi *v1.VirtualMachineInstance) string {
	return fmt.Sprintf("%v/%v", vmi.ObjectMeta.Namespace, vmi.ObjectMeta.Name)
}

func PodKey(pod *k8sv1.Pod) string {
	return fmt.Sprintf("%v/%v", pod.Namespace, pod.Name)
}

func DataVolumeKey(dataVolume *cdiv1.DataVolume) string {
	return fmt.Sprintf("%v/%v", dataVolume.Namespace, dataVolume.Name)
}

func VirtualMachineInstanceKeys(vmis []*v1.VirtualMachineInstance) []string {
	keys := []string{}
	for _, vmi := range vmis {
		keys = append(keys, VirtualMachineInstanceKey(vmi))
	}
	return keys
}

func HasFinalizer(object metav1.Object, finalizer string) bool {
	for _, f := range object.GetFinalizers() {
		if f == finalizer {
			return true
		}
	}
	return false
}

func RemoveFinalizer(object metav1.Object, finalizer string) {
	filtered := []string{}
	for _, f := range object.GetFinalizers() {
		if f != finalizer {
			filtered = append(filtered, f)
		}
	}
	object.SetFinalizers(filtered)
}

func AddFinalizer(object metav1.Object, finalizer string) {
	if HasFinalizer(object, finalizer) {
		return
	}
	object.SetFinalizers(append(object.GetFinalizers(), finalizer))
}

func ObservedLatestApiVersionAnnotation(object metav1.Object) bool {
	annotations := object.GetAnnotations()
	if annotations == nil {
		return false
	}

	version, ok := annotations[v1.ControllerAPILatestVersionObservedAnnotation]
	if !ok || version != v1.ApiLatestVersion {
		return false
	}
	return true
}

func SetLatestApiVersionAnnotation(object metav1.Object) {
	annotations := object.GetAnnotations()
	if annotations == nil {
		annotations = make(map[string]string)
	}

	annotations[v1.ControllerAPILatestVersionObservedAnnotation] = v1.ApiLatestVersion
	annotations[v1.ControllerAPIStorageVersionObservedAnnotation] = v1.ApiStorageVersion
	object.SetAnnotations(annotations)
}

func ApplyVolumeRequestOnVMISpec(vmiSpec *v1.VirtualMachineInstanceSpec, request *v1.VirtualMachineVolumeRequest) *v1.VirtualMachineInstanceSpec {
	if request.AddVolumeOptions != nil {
		alreadyAdded := false
		for _, volume := range vmiSpec.Volumes {
			if volume.Name == request.AddVolumeOptions.Name {
				alreadyAdded = true
				break
			}
		}

		if !alreadyAdded {
			newVolume := v1.Volume{
				Name: request.AddVolumeOptions.Name,
			}

			if request.AddVolumeOptions.VolumeSource.PersistentVolumeClaim != nil {
				pvcSource := request.AddVolumeOptions.VolumeSource.PersistentVolumeClaim.DeepCopy()
				pvcSource.Hotpluggable = true
				newVolume.VolumeSource.PersistentVolumeClaim = pvcSource
			} else if request.AddVolumeOptions.VolumeSource.DataVolume != nil {
				dvSource := request.AddVolumeOptions.VolumeSource.DataVolume.DeepCopy()
				dvSource.Hotpluggable = true
				newVolume.VolumeSource.DataVolume = dvSource
			}

			vmiSpec.Volumes = append(vmiSpec.Volumes, newVolume)

			if request.AddVolumeOptions.Disk != nil {
				newDisk := request.AddVolumeOptions.Disk.DeepCopy()
				newDisk.Name = request.AddVolumeOptions.Name

				vmiSpec.Domain.Devices.Disks = append(vmiSpec.Domain.Devices.Disks, *newDisk)
			}
		}

	} else if request.RemoveVolumeOptions != nil {

		newVolumesList := []v1.Volume{}
		newDisksList := []v1.Disk{}

		for _, volume := range vmiSpec.Volumes {
			if volume.Name != request.RemoveVolumeOptions.Name {
				newVolumesList = append(newVolumesList, volume)
			}
		}

		for _, disk := range vmiSpec.Domain.Devices.Disks {
			if disk.Name != request.RemoveVolumeOptions.Name {
				newDisksList = append(newDisksList, disk)
			}
		}

		vmiSpec.Volumes = newVolumesList
		vmiSpec.Domain.Devices.Disks = newDisksList
	}

	return vmiSpec
}

func CurrentVMIPod(vmi *v1.VirtualMachineInstance, podInformer cache.SharedIndexInformer) (*k8sv1.Pod, error) {

	// current pod is the most recent pod created on the current VMI node
	// OR the most recent pod created if no VMI node is set.

	// Get all pods from the namespace
	objs, err := podInformer.GetIndexer().ByIndex(cache.NamespaceIndex, vmi.Namespace)
	if err != nil {
		return nil, err
	}
	pods := []*k8sv1.Pod{}
	for _, obj := range objs {
		pod := obj.(*k8sv1.Pod)
		pods = append(pods, pod)
	}

	var curPod *k8sv1.Pod = nil
	for _, pod := range pods {
		if !IsControlledBy(pod, vmi) {
			continue
		}

		if vmi.Status.NodeName != "" &&
			vmi.Status.NodeName != pod.Spec.NodeName {
			// This pod isn't scheduled to the current node.
			// This can occur during the initial migration phases when
			// a new target node is being prepared for the VMI.
			continue
		}

		if curPod == nil || curPod.CreationTimestamp.Before(&pod.CreationTimestamp) {
			curPod = pod
		}
	}

	return curPod, nil
}

func VMIActivePodsCount(vmi *v1.VirtualMachineInstance, vmiPodInformer cache.SharedIndexInformer) int {

	objs, err := vmiPodInformer.GetIndexer().ByIndex(cache.NamespaceIndex, vmi.Namespace)
	if err != nil {
		return 0
	}

	running := 0
	for _, obj := range objs {
		pod := obj.(*k8sv1.Pod)

		if pod.Status.Phase == k8sv1.PodSucceeded || pod.Status.Phase == k8sv1.PodFailed {
			// not interested in terminated pods
			continue
		} else if !IsControlledBy(pod, vmi) {
			// not interested pods not associated with the vmi
			continue
		}
		running++
	}

	return running
}

func GeneratePatchBytes(ops []string) []byte {

	return []byte(fmt.Sprintf("[%s]", strings.Join(ops, ", ")))
}

func SetVMIPhaseTransitionTimestamp(oldVMI *v1.VirtualMachineInstance, newVMI *v1.VirtualMachineInstance) {
	if oldVMI.Status.Phase != newVMI.Status.Phase {
		for _, transitionTimeStamp := range newVMI.Status.PhaseTransitionTimestamps {
			if transitionTimeStamp.Phase == newVMI.Status.Phase {
				// already exists.
				return
			}
		}

		now := metav1.NewTime(time.Now())
		newVMI.Status.PhaseTransitionTimestamps = append(newVMI.Status.PhaseTransitionTimestamps, v1.VirtualMachineInstancePhaseTransitionTimestamp{
			Phase:                    newVMI.Status.Phase,
			PhaseTransitionTimestamp: now,
		})
	}
}

func VMIHasHotplugVolumes(vmi *v1.VirtualMachineInstance) bool {
	for _, volumeStatus := range vmi.Status.VolumeStatus {
		if volumeStatus.HotplugVolume != nil {
			return true
		}
	}
	for _, volume := range vmi.Spec.Volumes {
		if volume.DataVolume != nil && volume.DataVolume.Hotpluggable {
			return true
		}
		if volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.Hotpluggable {
			return true
		}
	}
	return false
}

func AttachmentPods(ownerPod *k8sv1.Pod, podInformer cache.SharedIndexInformer) ([]*k8sv1.Pod, error) {
	objs, err := podInformer.GetIndexer().ByIndex(cache.NamespaceIndex, ownerPod.Namespace)
	if err != nil {
		return nil, err
	}
	attachmentPods := []*k8sv1.Pod{}
	for _, obj := range objs {
		pod := obj.(*k8sv1.Pod)
		ownerRef := GetControllerOf(pod)
		if ownerRef == nil || ownerRef.UID != ownerPod.UID {
			continue
		}
		attachmentPods = append(attachmentPods, pod)
	}
	return attachmentPods, nil
}
<commit_msg>controller: add helper to escape JSON pointers<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright 2017 Red Hat, Inc.
 *
 */

package controller

import (
	"context"
	"fmt"
	"runtime/debug"
	"strings"
	"time"

	k8sv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/log"
	cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1beta1"
)

const (
	// BurstReplicas is the maximum amount of requests in a row for CRUD operations on resources by controllers,
	// to avoid unintentional DoS
	BurstReplicas uint = 250
)

// NewListWatchFromClient creates a new ListWatch from the specified client, resource, kubevirtNamespace and field selector.
func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch {
	listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
		options.FieldSelector = fieldSelector.String()
		options.LabelSelector = labelSelector.String()
		return c.Get().
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, metav1.ParameterCodec).
			Do(context.Background()).
			Get()
	}
	watchFunc := func(options metav1.ListOptions) (watch.Interface, error) {
		options.FieldSelector = fieldSelector.String()
		options.LabelSelector = labelSelector.String()
		return c.Get().
			Prefix("watch").
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, metav1.ParameterCodec).
			Watch(context.Background())
	}
	return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
}

func HandlePanic() {
	if r := recover(); r != nil {
		log.Log.Level(log.FATAL).Log("stacktrace", debug.Stack(), "msg", r)
	}
}

func NewResourceEventHandlerFuncsForWorkqueue(queue workqueue.RateLimitingInterface) cache.ResourceEventHandlerFuncs {
	return cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := KeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
		UpdateFunc: func(old interface{}, new interface{}) {
			key, err := KeyFunc(new)
			if err == nil {
				queue.Add(key)
			}
		},
		DeleteFunc: func(obj interface{}) {
			key, err := KeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
	}
}

func MigrationKey(migration *v1.VirtualMachineInstanceMigration) string {
	return fmt.Sprintf("%v/%v", migration.ObjectMeta.Namespace, migration.ObjectMeta.Name)
}

func VirtualMachineInstanceKey(vmi *v1.VirtualMachineInstance) string {
	return fmt.Sprintf("%v/%v", vmi.ObjectMeta.Namespace, vmi.ObjectMeta.Name)
}

func PodKey(pod *k8sv1.Pod) string {
	return fmt.Sprintf("%v/%v", pod.Namespace, pod.Name)
}

func DataVolumeKey(dataVolume *cdiv1.DataVolume) string {
	return fmt.Sprintf("%v/%v", dataVolume.Namespace, dataVolume.Name)
}

func VirtualMachineInstanceKeys(vmis []*v1.VirtualMachineInstance) []string {
	keys := []string{}
	for _, vmi := range vmis {
		keys = append(keys, VirtualMachineInstanceKey(vmi))
	}
	return keys
}

func HasFinalizer(object metav1.Object, finalizer string) bool {
	for _, f := range object.GetFinalizers() {
		if f == finalizer {
			return true
		}
	}
	return false
}

func RemoveFinalizer(object metav1.Object, finalizer string) {
	filtered := []string{}
	for _, f := range object.GetFinalizers() {
		if f != finalizer {
			filtered = append(filtered, f)
		}
	}
	object.SetFinalizers(filtered)
}

func AddFinalizer(object metav1.Object, finalizer string) {
	if HasFinalizer(object, finalizer) {
		return
	}
	object.SetFinalizers(append(object.GetFinalizers(), finalizer))
}

func ObservedLatestApiVersionAnnotation(object metav1.Object) bool {
	annotations := object.GetAnnotations()
	if annotations == nil {
		return false
	}

	version, ok := annotations[v1.ControllerAPILatestVersionObservedAnnotation]
	if !ok || version != v1.ApiLatestVersion {
		return false
	}
	return true
}

func SetLatestApiVersionAnnotation(object metav1.Object) {
	annotations := object.GetAnnotations()
	if annotations == nil {
		annotations = make(map[string]string)
	}

	annotations[v1.ControllerAPILatestVersionObservedAnnotation] = v1.ApiLatestVersion
	annotations[v1.ControllerAPIStorageVersionObservedAnnotation] = v1.ApiStorageVersion
	object.SetAnnotations(annotations)
}

func ApplyVolumeRequestOnVMISpec(vmiSpec *v1.VirtualMachineInstanceSpec, request *v1.VirtualMachineVolumeRequest) *v1.VirtualMachineInstanceSpec {
	if request.AddVolumeOptions != nil {
		alreadyAdded := false
		for _, volume := range vmiSpec.Volumes {
			if volume.Name == request.AddVolumeOptions.Name {
				alreadyAdded = true
				break
			}
		}

		if !alreadyAdded {
			newVolume := v1.Volume{
				Name: request.AddVolumeOptions.Name,
			}

			if request.AddVolumeOptions.VolumeSource.PersistentVolumeClaim != nil {
				pvcSource := request.AddVolumeOptions.VolumeSource.PersistentVolumeClaim.DeepCopy()
				pvcSource.Hotpluggable = true
				newVolume.VolumeSource.PersistentVolumeClaim = pvcSource
			} else if request.AddVolumeOptions.VolumeSource.DataVolume != nil {
				dvSource := request.AddVolumeOptions.VolumeSource.DataVolume.DeepCopy()
				dvSource.Hotpluggable = true
				newVolume.VolumeSource.DataVolume = dvSource
			}

			vmiSpec.Volumes = append(vmiSpec.Volumes, newVolume)

			if request.AddVolumeOptions.Disk != nil {
				newDisk := request.AddVolumeOptions.Disk.DeepCopy()
				newDisk.Name = request.AddVolumeOptions.Name

				vmiSpec.Domain.Devices.Disks = append(vmiSpec.Domain.Devices.Disks, *newDisk)
			}
		}

	} else if request.RemoveVolumeOptions != nil {

		newVolumesList := []v1.Volume{}
		newDisksList := []v1.Disk{}

		for _, volume := range vmiSpec.Volumes {
			if volume.Name != request.RemoveVolumeOptions.Name {
				newVolumesList = append(newVolumesList, volume)
			}
		}

		for _, disk := range vmiSpec.Domain.Devices.Disks {
			if disk.Name != request.RemoveVolumeOptions.Name {
				newDisksList = append(newDisksList, disk)
			}
		}

		vmiSpec.Volumes = newVolumesList
		vmiSpec.Domain.Devices.Disks = newDisksList
	}

	return vmiSpec
}

func CurrentVMIPod(vmi *v1.VirtualMachineInstance, podInformer cache.SharedIndexInformer) (*k8sv1.Pod, error) {

	// current pod is the most recent pod created on the current VMI node
	// OR the most recent pod created if no VMI node is set.

	// Get all pods from the namespace
	objs, err := podInformer.GetIndexer().ByIndex(cache.NamespaceIndex, vmi.Namespace)
	if err != nil {
		return nil, err
	}
	pods := []*k8sv1.Pod{}
	for _, obj := range objs {
		pod := obj.(*k8sv1.Pod)
		pods = append(pods, pod)
	}

	var curPod *k8sv1.Pod = nil
	for _, pod := range pods {
		if !IsControlledBy(pod, vmi) {
			continue
		}

		if vmi.Status.NodeName != "" &&
			vmi.Status.NodeName != pod.Spec.NodeName {
			// This pod isn't scheduled to the current node.
			// This can occur during the initial migration phases when
			// a new target node is being prepared for the VMI.
			continue
		}

		if curPod == nil || curPod.CreationTimestamp.Before(&pod.CreationTimestamp) {
			curPod = pod
		}
	}

	return curPod, nil
}

func VMIActivePodsCount(vmi *v1.VirtualMachineInstance, vmiPodInformer cache.SharedIndexInformer) int {

	objs, err := vmiPodInformer.GetIndexer().ByIndex(cache.NamespaceIndex, vmi.Namespace)
	if err != nil {
		return 0
	}

	running := 0
	for _, obj := range objs {
		pod := obj.(*k8sv1.Pod)

		if pod.Status.Phase == k8sv1.PodSucceeded || pod.Status.Phase == k8sv1.PodFailed {
			// not interested in terminated pods
			continue
		} else if !IsControlledBy(pod, vmi) {
			// not interested pods not associated with the vmi
			continue
		}
		running++
	}

	return running
}

func GeneratePatchBytes(ops []string) []byte {
	return []byte(fmt.Sprintf("[%s]", strings.Join(ops, ", ")))
}

func EscapeJSONPointer(ptr string) string {
	s := strings.ReplaceAll(ptr, "~", "~0")
	return strings.ReplaceAll(s, "/", "~1")
}

func SetVMIPhaseTransitionTimestamp(oldVMI *v1.VirtualMachineInstance, newVMI *v1.VirtualMachineInstance) {
	if oldVMI.Status.Phase != newVMI.Status.Phase {
		for _, transitionTimeStamp := range newVMI.Status.PhaseTransitionTimestamps {
			if transitionTimeStamp.Phase == newVMI.Status.Phase {
				// already exists.
				return
			}
		}

		now := metav1.NewTime(time.Now())
		newVMI.Status.PhaseTransitionTimestamps = append(newVMI.Status.PhaseTransitionTimestamps, v1.VirtualMachineInstancePhaseTransitionTimestamp{
			Phase:                    newVMI.Status.Phase,
			PhaseTransitionTimestamp: now,
		})
	}
}

func VMIHasHotplugVolumes(vmi *v1.VirtualMachineInstance) bool {
	for _, volumeStatus := range vmi.Status.VolumeStatus {
		if volumeStatus.HotplugVolume != nil {
			return true
		}
	}
	for _, volume := range vmi.Spec.Volumes {
		if volume.DataVolume != nil && volume.DataVolume.Hotpluggable {
			return true
		}
		if volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.Hotpluggable {
			return true
		}
	}
	return false
}

func AttachmentPods(ownerPod *k8sv1.Pod, podInformer cache.SharedIndexInformer) ([]*k8sv1.Pod, error) {
	objs, err := podInformer.GetIndexer().ByIndex(cache.NamespaceIndex, ownerPod.Namespace)
	if err != nil {
		return nil, err
	}
	attachmentPods := []*k8sv1.Pod{}
	for _, obj := range objs {
		pod := obj.(*k8sv1.Pod)
		ownerRef := GetControllerOf(pod)
		if ownerRef == nil || ownerRef.UID != ownerPod.UID {
			continue
		}
		attachmentPods = append(attachmentPods, pod)
	}
	return attachmentPods, nil
}
<|endoftext|>


<commit_before>package datasource

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"github.com/alexanderzobnin/grafana-zabbix/pkg/cache"
	"github.com/alexanderzobnin/grafana-zabbix/pkg/gtime"
	"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"

	"github.com/grafana/grafana-plugin-sdk-go/backend"
	"github.com/grafana/grafana-plugin-sdk-go/backend/log"
	"github.com/grafana/grafana-plugin-sdk-go/data"
)

type ZabbixDatasource struct {
	datasourceCache *cache.Cache
	logger          log.Logger
}

// ZabbixDatasourceInstance stores state about a specific datasource
// and provides methods to make requests to the Zabbix API
type ZabbixDatasourceInstance struct {
	zabbixAPI  *zabbixapi.ZabbixAPI
	dsInfo     *backend.DataSourceInstanceSettings
	Settings   *ZabbixDatasourceSettings
	queryCache *DatasourceCache
	logger     log.Logger
}

func NewZabbixDatasource() *ZabbixDatasource {
	return &ZabbixDatasource{
		datasourceCache: cache.NewCache(10*time.Minute, 10*time.Minute),
		logger:          log.New(),
	}
}

// NewZabbixDatasourceInstance returns an initialized zabbix datasource instance
func NewZabbixDatasourceInstance(dsInfo *backend.DataSourceInstanceSettings) (*ZabbixDatasourceInstance, error) {
	zabbixAPI, err := zabbixapi.New(dsInfo.URL)
	if err != nil {
		return nil, err
	}

	zabbixSettings, err := readZabbixSettings(dsInfo)
	if err != nil {
		return nil, err
	}

	return &ZabbixDatasourceInstance{
		dsInfo:     dsInfo,
		zabbixAPI:  zabbixAPI,
		Settings:   zabbixSettings,
		queryCache: NewDatasourceCache(zabbixSettings.CacheTTL, 10*time.Minute),
		logger:     log.New(),
	}, nil
}

// CheckHealth checks if the plugin is running properly
func (ds *ZabbixDatasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
	res := &backend.CheckHealthResult{}

	dsInstance, err := ds.GetDatasource(req.PluginContext)
	if err != nil {
		res.Status = backend.HealthStatusError
		res.Message = "Error getting datasource instance"
		ds.logger.Error("Error getting datasource instance", "err", err)
		return res, nil
	}

	message, err := dsInstance.TestConnection(ctx)
	if err != nil {
		res.Status = backend.HealthStatusError
		res.Message = err.Error()
		ds.logger.Error("Error connecting zabbix", "err", err)
		return res, nil
	}

	res.Status = backend.HealthStatusOk
	res.Message = message
	return res, nil
}

func (ds *ZabbixDatasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
	qdr := backend.NewQueryDataResponse()

	zabbixDS, err := ds.GetDatasource(req.PluginContext)
	if err != nil {
		return nil, err
	}

	for _, q := range req.Queries {
		res := backend.DataResponse{}
		query, err := ReadQuery(q)
		ds.logger.Debug("DS query", "query", q)
		if err != nil {
			res.Error = err
		} else if len(query.Functions) > 0 {
			res.Error = errors.New("Zabbix queries with functions are not supported")
		} else if query.Mode != 0 {
			res.Error = errors.New("Non-metrics queries are not supported")
		} else {
			frame, err := zabbixDS.queryNumericItems(ctx, &query)
			if err != nil {
				res.Error = err
			} else {
				res.Frames = []*data.Frame{frame}
			}
		}
		qdr.Responses[q.RefID] = res
	}

	return qdr, nil
}

// GetDatasource Returns cached datasource or creates new one
func (ds *ZabbixDatasource) GetDatasource(pluginContext backend.PluginContext) (*ZabbixDatasourceInstance, error) {
	dsSettings := pluginContext.DataSourceInstanceSettings
	dsKey := fmt.Sprintf("%d-%d", pluginContext.OrgID, dsSettings.ID)
	// Get hash to check if settings changed
	dsInfoHash := HashDatasourceInfo(dsSettings)

	if cachedData, ok := ds.datasourceCache.Get(dsKey); ok {
		if cachedDS, ok := cachedData.(*ZabbixDatasourceInstance); ok {
			cachedDSHash := HashDatasourceInfo(cachedDS.dsInfo)
			if cachedDSHash == dsInfoHash {
				return cachedDS, nil
			}
			ds.logger.Debug("Data source settings changed", "org", pluginContext.OrgID, "id", dsSettings.ID, "name", dsSettings.Name)
		}
	}

	ds.logger.Debug("Initializing data source", "org", pluginContext.OrgID, "id", dsSettings.ID, "name", dsSettings.Name)
	dsInstance, err := NewZabbixDatasourceInstance(pluginContext.DataSourceInstanceSettings)
	if err != nil {
		ds.logger.Error("Error initializing datasource", "error", err)
		return nil, err
	}

	ds.datasourceCache.Set(dsKey, dsInstance)
	return dsInstance, nil
}

func readZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings) (*ZabbixDatasourceSettings, error) {
	zabbixSettingsDTO := &ZabbixDatasourceSettingsDTO{}

	err := json.Unmarshal(dsInstanceSettings.JSONData, &zabbixSettingsDTO)
	if err != nil {
		return nil, err
	}

	if zabbixSettingsDTO.TrendsFrom == "" {
		zabbixSettingsDTO.TrendsFrom = "7d"
	}
	if zabbixSettingsDTO.TrendsRange == "" {
		zabbixSettingsDTO.TrendsRange = "4d"
	}
	if zabbixSettingsDTO.CacheTTL == "" {
		zabbixSettingsDTO.CacheTTL = "1h"
	}

	trendsFrom, err := gtime.ParseInterval(zabbixSettingsDTO.TrendsFrom)
	if err != nil {
		return nil, err
	}

	trendsRange, err := gtime.ParseInterval(zabbixSettingsDTO.TrendsRange)
	if err != nil {
		return nil, err
	}

	cacheTTL, err := gtime.ParseInterval(zabbixSettingsDTO.CacheTTL)
	if err != nil {
		return nil, err
	}

	zabbixSettings := &ZabbixDatasourceSettings{
		Trends:      zabbixSettingsDTO.Trends,
		TrendsFrom:  trendsFrom,
		TrendsRange: trendsRange,
		CacheTTL:    cacheTTL,
	}

	return zabbixSettings, nil
}
<commit_msg>refactor: errors<commit_after>package datasource

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"github.com/alexanderzobnin/grafana-zabbix/pkg/cache"
	"github.com/alexanderzobnin/grafana-zabbix/pkg/gtime"
	"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"

	"github.com/grafana/grafana-plugin-sdk-go/backend"
	"github.com/grafana/grafana-plugin-sdk-go/backend/log"
	"github.com/grafana/grafana-plugin-sdk-go/data"
)

var (
	ErrFunctionsNotSupported      = errors.New("zabbix queries with functions are not supported")
	ErrNonMetricQueryNotSupported = errors.New("non-metrics queries are not supported")
)

type ZabbixDatasource struct {
	datasourceCache *cache.Cache
	logger          log.Logger
}

// ZabbixDatasourceInstance stores state about a specific datasource
// and provides methods to make requests to the Zabbix API
type ZabbixDatasourceInstance struct {
	zabbixAPI  *zabbixapi.ZabbixAPI
	dsInfo     *backend.DataSourceInstanceSettings
	Settings   *ZabbixDatasourceSettings
	queryCache *DatasourceCache
	logger     log.Logger
}

func NewZabbixDatasource() *ZabbixDatasource {
	return &ZabbixDatasource{
		datasourceCache: cache.NewCache(10*time.Minute, 10*time.Minute),
		logger:          log.New(),
	}
}

// NewZabbixDatasourceInstance returns an initialized zabbix datasource instance
func NewZabbixDatasourceInstance(dsInfo *backend.DataSourceInstanceSettings) (*ZabbixDatasourceInstance, error) {
	zabbixAPI, err := zabbixapi.New(dsInfo.URL)
	if err != nil {
		return nil, err
	}

	zabbixSettings, err := readZabbixSettings(dsInfo)
	if err != nil {
		return nil, err
	}

	return &ZabbixDatasourceInstance{
		dsInfo:     dsInfo,
		zabbixAPI:  zabbixAPI,
		Settings:   zabbixSettings,
		queryCache: NewDatasourceCache(zabbixSettings.CacheTTL, 10*time.Minute),
		logger:     log.New(),
	}, nil
}

// CheckHealth checks if the plugin is running properly
func (ds *ZabbixDatasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
	res := &backend.CheckHealthResult{}

	dsInstance, err := ds.GetDatasource(req.PluginContext)
	if err != nil {
		res.Status = backend.HealthStatusError
		res.Message = "Error getting datasource instance"
		ds.logger.Error("Error getting datasource instance", "err", err)
		return res, nil
	}

	message, err := dsInstance.TestConnection(ctx)
	if err != nil {
		res.Status = backend.HealthStatusError
		res.Message = err.Error()
		ds.logger.Error("Error connecting zabbix", "err", err)
		return res, nil
	}

	res.Status = backend.HealthStatusOk
	res.Message = message
	return res, nil
}

func (ds *ZabbixDatasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
	qdr := backend.NewQueryDataResponse()

	zabbixDS, err := ds.GetDatasource(req.PluginContext)
	if err != nil {
		return nil, err
	}

	for _, q := range req.Queries {
		res := backend.DataResponse{}
		query, err := ReadQuery(q)
		ds.logger.Debug("DS query", "query", q)
		if err != nil {
			res.Error = err
		} else if len(query.Functions) > 0 {
			res.Error = ErrFunctionsNotSupported
		} else if query.Mode != 0 {
			res.Error = ErrNonMetricQueryNotSupported
		} else {
			frame, err := zabbixDS.queryNumericItems(ctx, &query)
			if err != nil {
				res.Error = err
			} else {
				res.Frames = []*data.Frame{frame}
			}
		}
		qdr.Responses[q.RefID] = res
	}

	return qdr, nil
}

// GetDatasource Returns cached datasource or creates new one
func (ds *ZabbixDatasource) GetDatasource(pluginContext backend.PluginContext) (*ZabbixDatasourceInstance, error) {
	dsSettings := pluginContext.DataSourceInstanceSettings
	dsKey := fmt.Sprintf("%d-%d", pluginContext.OrgID, dsSettings.ID)
	// Get hash to check if settings changed
	dsInfoHash := HashDatasourceInfo(dsSettings)

	if cachedData, ok := ds.datasourceCache.Get(dsKey); ok {
		if cachedDS, ok := cachedData.(*ZabbixDatasourceInstance); ok {
			cachedDSHash := HashDatasourceInfo(cachedDS.dsInfo)
			if cachedDSHash == dsInfoHash {
				return cachedDS, nil
			}
			ds.logger.Debug("Data source settings changed", "org", pluginContext.OrgID, "id", dsSettings.ID, "name", dsSettings.Name)
		}
	}

	ds.logger.Debug("Initializing data source", "org", pluginContext.OrgID, "id", dsSettings.ID, "name", dsSettings.Name)
	dsInstance, err := NewZabbixDatasourceInstance(pluginContext.DataSourceInstanceSettings)
	if err != nil {
		ds.logger.Error("Error initializing datasource", "error", err)
		return nil, err
	}

	ds.datasourceCache.Set(dsKey, dsInstance)
	return dsInstance, nil
}

func readZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings) (*ZabbixDatasourceSettings, error) {
	zabbixSettingsDTO := &ZabbixDatasourceSettingsDTO{}

	err := json.Unmarshal(dsInstanceSettings.JSONData, &zabbixSettingsDTO)
	if err != nil {
		return nil, err
	}

	if zabbixSettingsDTO.TrendsFrom == "" {
		zabbixSettingsDTO.TrendsFrom = "7d"
	}
	if zabbixSettingsDTO.TrendsRange == "" {
		zabbixSettingsDTO.TrendsRange = "4d"
	}
	if zabbixSettingsDTO.CacheTTL == "" {
		zabbixSettingsDTO.CacheTTL = "1h"
	}

	trendsFrom, err := gtime.ParseInterval(zabbixSettingsDTO.TrendsFrom)
	if err != nil {
		return nil, err
	}

	trendsRange, err := gtime.ParseInterval(zabbixSettingsDTO.TrendsRange)
	if err != nil {
		return nil, err
	}

	cacheTTL, err := gtime.ParseInterval(zabbixSettingsDTO.CacheTTL)
	if err != nil {
		return nil, err
	}

	zabbixSettings := &ZabbixDatasourceSettings{
		Trends:      zabbixSettingsDTO.Trends,
		TrendsFrom:  trendsFrom,
		TrendsRange: trendsRange,
		CacheTTL:    cacheTTL,
	}

	return zabbixSettings, nil
}
<|endoftext|>


<commit_before>/*
Copyright © 2014–5 Brad Ackerman.

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 dbaccess

import "strings"

// Our hand-crafted SQL statements.
var (
	materialComposition = `
  SELECT mt."typeID" AS "materialID", m."quantity" AS "quantity"
  FROM "invTypes" t, "invTypes" mt, "invTypeMaterials" m
  WHERE t."typeID" = ?
  AND t."typeID" = m."typeID"
  AND mt."typeID" = m."materialTypeID"
  `
	itemInfo = `
  SELECT t."typeID", t."typeName", t."portionSize", g."groupID", g."groupName", c."categoryName"
  FROM "invTypes" t, "invCategories" c, "invGroups" g
  WHERE t."typeName" = ? AND t."groupID" = g."groupID"
  AND   g."categoryID" = c."categoryID"
  `
	itemIDInfo = `
  SELECT t."typeID", t."typeName", t."portionSize", g."groupID", g."groupName", c."categoryName"
  FROM "invTypes" t, "invCategories" c, "invGroups" g
  WHERE t."typeID" = ? AND t."groupID" = g."groupID"
  AND   g."categoryID" = c."categoryID"
  `
	catTree = `
  WITH RECURSIVE
  parents("marketGroupID", "parentGroupID") AS
  (
    SELECT "marketGroupID", "parentGroupID" FROM "invMarketGroups"
    WHERE "marketGroupID" = (
      SELECT "marketGroupID"
      FROM "invTypes" i
      JOIN "invMarketGroups" m USING("marketGroupID")
      WHERE i."typeID" = ?
      )
      UNION ALL
      SELECT mg."marketGroupID", mg."parentGroupID"
      FROM "invMarketGroups" mg
      INNER JOIN parents p ON mg."marketGroupID"=p."parentGroupID"
      )
      SELECT p."marketGroupID", m1."marketGroupName", m1."description", p."parentGroupID", m2."marketGroupName", m2."description"
      FROM parents p
      JOIN "invMarketGroups" m1 ON p."marketGroupID" = m1."marketGroupID"
      JOIN "invMarketGroups" m2 ON p."parentGroupID" = m2."marketGroupID"
      `

	systemInfo = `
      SELECT   s."solarSystemName", s."solarSystemID", s."security",
               c."constellationName", c."constellationID", r."regionName", r."regionID"
      FROM     "mapSolarSystems" s
      JOIN     "mapConstellations" c USING("constellationID")
      JOIN     "mapRegions" r ON r."regionID" = c."regionID"
      WHERE    LOWER(s."solarSystemName") LIKE LOWER(?)
			ORDER BY s."solarSystemName"
      `

	systemIDInfo = `
      SELECT s."solarSystemName", s."solarSystemID", s."security",
             c."constellationName", c."constellationID", r."regionName", r."regionID"
      FROM   "mapSolarSystems" s
      JOIN   "mapConstellations" c USING("constellationID")
      JOIN   "mapRegions" r ON r."regionID" = c."regionID"
      WHERE  s."solarSystemID" = ?
      `

	regionInfo = `
      SELECT "regionID", "regionName"
      FROM   "mapRegions"
      WHERE  "regionName" = ?
      `

	stationIDInfo = `
      SELECT "stationName", "stationID", "solarSystemID", "constellationID", "regionID",
						 "corporationID", "itemName" "corporationName", "reprocessingEfficiency"
      FROM   "staStations" s
			JOIN   "invNames" n ON n."itemID" = s."corporationID"
      WHERE  "stationID" = ?
      `
	stationNameInfo = `
		SELECT "stationName", "stationID", "solarSystemID", "constellationID", "regionID",
					 "corporationID", "itemName" "corporationName", "reprocessingEfficiency"
		FROM   "staStations" s
		JOIN   "invNames" n ON n."itemID" = s."corporationID"
		WHERE  LOWER("stationName") LIKE LOWER(?)
		ORDER BY "stationName"
		`

	blueprintBase = `
		SELECT ti."typeName" "inputItem", ram."activityName", tyo."typeName" "outputProduct",
		       iap."quantity" "outputProductQty"
		FROM   "industryActivityProducts" iap
		JOIN   "invTypes" ti USING("typeID")
		JOIN   "ramActivities" ram USING("activityID")
		JOIN   "invTypes" tyo ON iap."productTypeID" = tyo."typeID"
		WHERE  QUERYCOLUMN LIKE ?
		ORDER BY "inputItem", "outputProduct"
		`

	// What items can I produce with a blueprint?
	blueprintProduces = strings.Replace(blueprintBase, "QUERYCOLUMN", "ti.\"typeName\"", 1)

	// How can I produce a blueprint?
	blueprintProducedBy = strings.Replace(blueprintBase, "QUERYCOLUMN", "tyo.\"typeName\"", 1)

	// Extra stanzas for WHERE when querying on input materials
	inputMatsWhere = `
		JOIN   "industryActivityMaterials" iam
		ON     iam."typeID" = ti."typeID"
		JOIN   "invTypes" tm
		ON     iam."materialTypeID" = tm."typeID"
	`
	inputMaterialsToBlueprint = strings.Replace(
		strings.Replace(blueprintBase, "WHERE", inputMatsWhere+" WHERE ", 1),
		"QUERYCOLUMN", "tm.\"typeName\"", 1)

	// Given a blueprint, what items do I need to manufacture/invent with it?
	materialsForBlueprintProduction = `
		SELECT ti."typeName" "inputItem", "activityName", tm."typeName" "inputMaterial",
					 iam."quantity" "inputMaterialQty", tyo."typeName" "outputProduct",
					 iap."quantity" "outputProductQty"
		FROM   "industryActivityMaterials" iam
		JOIN   "invTypes" ti USING("typeID")
		JOIN   "invTypes" tm
		ON     iam."materialTypeID" = tm."typeID"
		JOIN   "ramActivities" USING("activityID")
		JOIN   "industryActivityProducts" iap
		ON     iap."typeID" = ti."typeID" AND iap."activityID"=iam."activityID"
		JOIN   "invTypes" tyo ON iap."productTypeID" = tyo."typeID"
		WHERE  ti."typeName" = ? AND tyo."typeName" = ?
		ORDER BY "inputItem", "outputProduct", "inputMaterial"
		`

	// What are the possible outputs from reprocessing an item?
	reprocessOutputsStmt = `
		SELECT t_mat."typeID"
		FROM   "invTypes" t_mat
		JOIN   "invTypeMaterials" tm ON tm."materialTypeID" = t_mat."typeID"
		JOIN   "invTypes" t_prod ON tm."typeID" = t_prod."typeID"
		WHERE  t_prod."marketGroupID" IS NOT NULL
		GROUP BY t_mat."typeID"
		ORDER BY t_mat."typeName"
		`
)
<commit_msg>Fix for SQL conformance / pgsql<commit_after>/*
Copyright © 2014–5 Brad Ackerman.

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 dbaccess

import "strings"

// Our hand-crafted SQL statements.
var (
	materialComposition = `
  SELECT mt."typeID" AS "materialID", m."quantity" AS "quantity"
  FROM "invTypes" t, "invTypes" mt, "invTypeMaterials" m
  WHERE t."typeID" = ?
  AND t."typeID" = m."typeID"
  AND mt."typeID" = m."materialTypeID"
  `
	itemInfo = `
  SELECT t."typeID", t."typeName", t."portionSize", g."groupID", g."groupName", c."categoryName"
  FROM "invTypes" t, "invCategories" c, "invGroups" g
  WHERE t."typeName" = ? AND t."groupID" = g."groupID"
  AND   g."categoryID" = c."categoryID"
  `
	itemIDInfo = `
  SELECT t."typeID", t."typeName", t."portionSize", g."groupID", g."groupName", c."categoryName"
  FROM "invTypes" t, "invCategories" c, "invGroups" g
  WHERE t."typeID" = ? AND t."groupID" = g."groupID"
  AND   g."categoryID" = c."categoryID"
  `
	catTree = `
  WITH RECURSIVE
  parents("marketGroupID", "parentGroupID") AS
  (
    SELECT "marketGroupID", "parentGroupID" FROM "invMarketGroups"
    WHERE "marketGroupID" = (
      SELECT "marketGroupID"
      FROM "invTypes" i
      JOIN "invMarketGroups" m USING("marketGroupID")
      WHERE i."typeID" = ?
      )
      UNION ALL
      SELECT mg."marketGroupID", mg."parentGroupID"
      FROM "invMarketGroups" mg
      INNER JOIN parents p ON mg."marketGroupID"=p."parentGroupID"
      )
      SELECT p."marketGroupID", m1."marketGroupName", m1."description", p."parentGroupID", m2."marketGroupName", m2."description"
      FROM parents p
      JOIN "invMarketGroups" m1 ON p."marketGroupID" = m1."marketGroupID"
      JOIN "invMarketGroups" m2 ON p."parentGroupID" = m2."marketGroupID"
      `

	systemInfo = `
      SELECT   s."solarSystemName", s."solarSystemID", s."security",
               c."constellationName", c."constellationID", r."regionName", r."regionID"
      FROM     "mapSolarSystems" s
      JOIN     "mapConstellations" c USING("constellationID")
      JOIN     "mapRegions" r ON r."regionID" = c."regionID"
      WHERE    LOWER(s."solarSystemName") LIKE LOWER(?)
			ORDER BY s."solarSystemName"
      `

	systemIDInfo = `
      SELECT s."solarSystemName", s."solarSystemID", s."security",
             c."constellationName", c."constellationID", r."regionName", r."regionID"
      FROM   "mapSolarSystems" s
      JOIN   "mapConstellations" c USING("constellationID")
      JOIN   "mapRegions" r ON r."regionID" = c."regionID"
      WHERE  s."solarSystemID" = ?
      `

	regionInfo = `
      SELECT "regionID", "regionName"
      FROM   "mapRegions"
      WHERE  "regionName" = ?
      `

	stationIDInfo = `
      SELECT "stationName", "stationID", "solarSystemID", "constellationID", "regionID",
						 "corporationID", "itemName" "corporationName", "reprocessingEfficiency"
      FROM   "staStations" s
			JOIN   "invNames" n ON n."itemID" = s."corporationID"
      WHERE  "stationID" = ?
      `
	stationNameInfo = `
		SELECT "stationName", "stationID", "solarSystemID", "constellationID", "regionID",
					 "corporationID", "itemName" "corporationName", "reprocessingEfficiency"
		FROM   "staStations" s
		JOIN   "invNames" n ON n."itemID" = s."corporationID"
		WHERE  LOWER("stationName") LIKE LOWER(?)
		ORDER BY "stationName"
		`

	blueprintBase = `
		SELECT ti."typeName" "inputItem", ram."activityName", tyo."typeName" "outputProduct",
		       iap."quantity" "outputProductQty"
		FROM   "industryActivityProducts" iap
		JOIN   "invTypes" ti USING("typeID")
		JOIN   "ramActivities" ram USING("activityID")
		JOIN   "invTypes" tyo ON iap."productTypeID" = tyo."typeID"
		WHERE  QUERYCOLUMN LIKE ?
		ORDER BY "inputItem", "outputProduct"
		`

	// What items can I produce with a blueprint?
	blueprintProduces = strings.Replace(blueprintBase, "QUERYCOLUMN", "ti.\"typeName\"", 1)

	// How can I produce a blueprint?
	blueprintProducedBy = strings.Replace(blueprintBase, "QUERYCOLUMN", "tyo.\"typeName\"", 1)

	// Extra stanzas for WHERE when querying on input materials
	inputMatsWhere = `
		JOIN   "industryActivityMaterials" iam
		ON     iam."typeID" = ti."typeID"
		JOIN   "invTypes" tm
		ON     iam."materialTypeID" = tm."typeID"
	`
	inputMaterialsToBlueprint = strings.Replace(
		strings.Replace(blueprintBase, "WHERE", inputMatsWhere+" WHERE ", 1),
		"QUERYCOLUMN", "tm.\"typeName\"", 1)

	// Given a blueprint, what items do I need to manufacture/invent with it?
	materialsForBlueprintProduction = `
		SELECT ti."typeName" "inputItem", "activityName", tm."typeName" "inputMaterial",
					 iam."quantity" "inputMaterialQty", tyo."typeName" "outputProduct",
					 iap."quantity" "outputProductQty"
		FROM   "industryActivityMaterials" iam
		JOIN   "invTypes" ti USING("typeID")
		JOIN   "invTypes" tm
		ON     iam."materialTypeID" = tm."typeID"
		JOIN   "ramActivities" USING("activityID")
		JOIN   "industryActivityProducts" iap
		ON     iap."typeID" = ti."typeID" AND iap."activityID"=iam."activityID"
		JOIN   "invTypes" tyo ON iap."productTypeID" = tyo."typeID"
		WHERE  ti."typeName" = ? AND tyo."typeName" = ?
		ORDER BY "inputItem", "outputProduct", "inputMaterial"
		`

	// What are the possible outputs from reprocessing an item?
	reprocessOutputsStmt = `
		SELECT t_mat."typeID"
		FROM   "invTypes" t_mat
		JOIN   "invTypeMaterials" tm ON tm."materialTypeID" = t_mat."typeID"
		JOIN   "invTypes" t_prod ON tm."typeID" = t_prod."typeID"
		WHERE  t_prod."marketGroupID" IS NOT NULL
		GROUP BY t_mat."typeID", t_mat."typeName"
		ORDER BY t_mat."typeName"
		`
)
<|endoftext|>


<commit_before>package acme

import (
	"context"
	"testing"

	"github.com/jetstack/cert-manager/pkg/acme/client"
	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/pkg/controller/test"
)

const (
	defaultTestAcmeClusterResourceNamespace = "default"
	defaultTestSolverImage                  = "fake-solver-image"
)

type acmeFixture struct {
	Acme *Acme
	*test.Builder

	Issuer      v1alpha1.GenericIssuer
	Certificate *v1alpha1.Certificate
	Client      *client.FakeACME

	PreFn   func(*acmeFixture)
	CheckFn func(*acmeFixture, ...interface{})
	Err     bool

	Ctx context.Context
}

func (s *acmeFixture) Setup(t *testing.T) {
	if s.Client == nil {
		s.Client = &client.FakeACME{}
	}
	if s.Ctx == nil {
		s.Ctx = context.Background()
	}
	if s.Builder == nil {
		s.Builder = &test.Builder{
			// TODO: set default IssuerOptions
			//		defaultTestAcmeClusterResourceNamespace,
			//		defaultTestSolverImage,
			//		default dns01 nameservers
			//		ambient credentials settings
		}
	}
	s.Acme = buildFakeAcme(s.Builder, s.Issuer)
	if s.PreFn != nil {
		s.PreFn(s)
		s.Builder.Sync()
	}
}

func (s *acmeFixture) Finish(t *testing.T, args ...interface{}) {
	defer s.Builder.Stop()
	// resync listers before running checks
	s.Builder.Sync()
	// run custom checks
	if s.CheckFn != nil {
		s.CheckFn(s, args...)
	}
}

func buildFakeAcme(b *test.Builder, issuer v1alpha1.GenericIssuer) *Acme {
	b.Start()
	a, err := New(b.Context, issuer)
	if err != nil {
		panic("error creating fake Acme: %v" + err.Error())
	}
	b.Sync()
	return a.(*Acme)
}
<commit_msg>Fix resourceNamespace<commit_after>package acme

import (
	"context"
	"testing"

	"github.com/jetstack/cert-manager/pkg/acme/client"
	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/pkg/controller/test"
)

const (
	defaultTestAcmeClusterResourceNamespace = "default"
	defaultTestSolverImage                  = "fake-solver-image"
)

type acmeFixture struct {
	Acme *Acme
	*test.Builder

	Issuer      v1alpha1.GenericIssuer
	Certificate *v1alpha1.Certificate
	Client      *client.FakeACME

	PreFn   func(*acmeFixture)
	CheckFn func(*acmeFixture, ...interface{})
	Err     bool

	Ctx context.Context
}

func (s *acmeFixture) Setup(t *testing.T) {
	if s.Client == nil {
		s.Client = &client.FakeACME{}
	}
	if s.Ctx == nil {
		s.Ctx = context.Background()
	}
	if s.Builder == nil {
		// TODO: set default IssuerOptions
		//		defaultTestAcmeClusterResourceNamespace,
		//		defaultTestSolverImage,
		//		default dns01 nameservers
		//		ambient credentials settings
		s.Builder = &test.Builder{}
	}
	s.Acme = buildFakeAcme(s.Builder, s.Issuer)
	if s.PreFn != nil {
		s.PreFn(s)
		s.Builder.Sync()
	}
}

func (s *acmeFixture) Finish(t *testing.T, args ...interface{}) {
	defer s.Builder.Stop()
	// resync listers before running checks
	s.Builder.Sync()
	// run custom checks
	if s.CheckFn != nil {
		s.CheckFn(s, args...)
	}
}

func buildFakeAcme(b *test.Builder, issuer v1alpha1.GenericIssuer) *Acme {
	b.Start()
	a, err := New(b.Context, issuer)
	if err != nil {
		panic("error creating fake Acme: %v" + err.Error())
	}
	b.Sync()
	return a.(*Acme)
}
<|endoftext|>


<commit_before>package kmodule

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"

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

// Flags to finit_module(2) / FileInit.
const (
	// Ignore symbol version hashes.
	MODULE_INIT_IGNORE_MODVERSIONS = 0x1

	// Ignore kernel version magic.
	MODULE_INIT_IGNORE_VERMAGIC = 0x2

)

// SyscallError contains an error message as well as the actual syscall Errno
type SyscallError struct {
	Msg   string
	Errno syscall.Errno
}

func (s *SyscallError) Error() string {
	if s.Errno != 0 {
		return fmt.Sprintf("%s: %v", s.Msg, s.Errno)
	}
	return s.Msg
}

// Init loads the kernel module given by image with the given options.
func Init(image []byte, opts string) error {
	optsNull, err := unix.BytePtrFromString(opts)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("kmodule.Init: could not convert %q to C string: %v", opts, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_INIT_MODULE, uintptr(unsafe.Pointer(&image[0])), uintptr(len(image)), uintptr(unsafe.Pointer(optsNull))); e != 0 {
		return &SyscallError{
			Msg:   fmt.Sprintf("init_module(%v, %q) failed", image, opts),
			Errno: e,
		}
	}

	return nil
}

// FileInit loads the kernel module contained by `f` with the given opts and
// flags.
//
// FileInit falls back to Init when the finit_module(2) syscall is not available.
func FileInit(f *os.File, opts string, flags uintptr) error {
	optsNull, err := unix.BytePtrFromString(opts)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("kmodule.Init: could not convert %q to C string: %v", opts, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_FINIT_MODULE, f.Fd(), uintptr(unsafe.Pointer(optsNull)), flags); e == unix.ENOSYS {
		if flags != 0 {
			return &SyscallError{Msg: fmt.Sprintf("finit_module unavailable"), Errno: e}
		}

		// Fall back to regular init_module(2).
		img, err := ioutil.ReadAll(f)
		if err != nil {
			return &SyscallError{Msg: fmt.Sprintf("kmodule.FileInit: %v", err)}
		}
		return Init(img, opts)
	} else if e != 0 {
		return &SyscallError{
			Msg:   fmt.Sprintf("finit_module(%v, %q, %#x) failed", f, opts, flags),
			Errno: e,
		}
	}

	return nil
}

// Delete removes a kernel module.
func Delete(name string, flags uintptr) error {
	modnameptr, err := unix.BytePtrFromString(name)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not delete module %q: %v", name, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_DELETE_MODULE, uintptr(unsafe.Pointer(modnameptr)), flags, 0); e != 0 {
		return &SyscallError{Msg: fmt.Sprintf("could not delete module %q", name), Errno: e}
	}

	return nil
}

type modState uint8

const (
	unloaded modState = iota
	loading
	loaded
)

type dependency struct {
	state modState
	deps  []string
}

type depMap map[string]*dependency

// ProbeOpts contains optional parameters to Probe.
//
// An empty ProbeOpts{} should lead to the default behavior.
type ProbeOpts struct {
	DryRun bool
}

// Probe loads the given kernel module and its dependencies.
// It is calls ProbeOptions with the default ProbeOpts.
func Probe(name string, modParams string) error {
	return ProbeOptions(name, modParams, ProbeOpts{})
}

// ProbeOptions loads the given kernel module and its dependencies.
// This functions takes ProbeOpts.
func ProbeOptions(name, modParams string, opts ProbeOpts) error {
	deps, err := genDeps()
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not generate dependency map %v", err)}
	}

	modPath, err := findModPath(name, deps)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not find module path %q: %v", name, err)}
	}

	if !opts.DryRun {
		// if the module is already loaded or does not have deps, or all of them are loaded
		// then this succeeds and we are done
		if err := loadModule(modPath, modParams, opts); err == nil {
			return nil
		}
		// okay, we have to try the hard way and load dependencies first.
	} else {
		fmt.Println("Unique dependencies in load order, already loaded ones get skipped:")
	}

	deps[modPath].state = loading
	for _, d := range deps[modPath].deps {
		if err := loadDeps(d, deps, opts); err != nil {
			return err
		}
	}
	if err := loadModule(modPath, modParams, opts); err != nil {
		return err
	}
	// we don't care to set the state to loaded
	// deps[modPath].state = loaded
	return nil
}

func genDeps() (depMap, error) {
	deps := make(depMap)

	var u unix.Utsname
	if err := unix.Uname(&u); err != nil {
		return nil, fmt.Errorf("could not get release (uname -r): %v", err)
	}
	rel := string(u.Release[:bytes.IndexByte(u.Release[:], 0)])

	moduleDirs := []string{"/lib/modules", "/usr/lib/modules"}

	var moduleDir string
	for _, moduleDirs := range(moduleDirs) {
		moduleDir = filepath.Join(moduleDirs, strings.TrimSpace(rel))
		if _, err := os.Stat(moduleDir); err == nil {
			break
		}
	}

	f, err := os.Open(filepath.Join(moduleDir, "modules.dep"))
	if err != nil {
		return nil, fmt.Errorf("could not open dependency file: %v", err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		nameDeps := strings.Split(txt, ":")
		modPath, modDeps := nameDeps[0], nameDeps[1]
		modPath = filepath.Join(moduleDir, strings.TrimSpace(modPath))

		var dependency dependency
		if len(modDeps) > 0 {
			for _, dep := range strings.Split(strings.TrimSpace(modDeps), " ") {
				dependency.deps = append(dependency.deps, filepath.Join(moduleDir, dep))
			}
		}
		deps[modPath] = &dependency
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return deps, nil
}

func findModPath(name string, m depMap) (string, error) {
	for mp := range m {
		if path.Base(mp) == name+".ko" {
			return mp, nil
		}
	}

	return "", fmt.Errorf("Could not find path for module %q", name)
}

func loadDeps(path string, m depMap, opts ProbeOpts) error {
	dependency, ok := m[path]
	if !ok {
		return &SyscallError{Msg: fmt.Sprintf("could not find dependency %q", path)}
	}

	if dependency.state == loading {
		return &SyscallError{Msg: fmt.Sprintf("circular dependency! %q already LOADING", path)}
	} else if dependency.state == loaded {
		return nil
	}

	m[path].state = loading

	for _, dep := range dependency.deps {
		if err := loadDeps(dep, m, opts); err != nil {
			return err
		}
	}

	// done with dependencies, load module
	if err := loadModule(path, "", opts); err != nil {
		return err
	}
	m[path].state = loaded

	return nil
}

func loadModule(path, modParams string, opts ProbeOpts) error {
	if opts.DryRun {
		fmt.Println(path)
		return nil
	}

	f, err := os.Open(path)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not open %q: %v", path, err)}
	}
	defer f.Close()

	if err := FileInit(f, modParams, 0); err != nil {
		if serr, ok := err.(*SyscallError); !ok || (ok && serr.Errno != unix.EEXIST) {
			return err
		}
	}

	return nil
}

<commit_msg>Nit remote empty line<commit_after>package kmodule

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"

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

// Flags to finit_module(2) / FileInit.
const (
	// Ignore symbol version hashes.
	MODULE_INIT_IGNORE_MODVERSIONS = 0x1

	// Ignore kernel version magic.
	MODULE_INIT_IGNORE_VERMAGIC = 0x2
)

// SyscallError contains an error message as well as the actual syscall Errno
type SyscallError struct {
	Msg   string
	Errno syscall.Errno
}

func (s *SyscallError) Error() string {
	if s.Errno != 0 {
		return fmt.Sprintf("%s: %v", s.Msg, s.Errno)
	}
	return s.Msg
}

// Init loads the kernel module given by image with the given options.
func Init(image []byte, opts string) error {
	optsNull, err := unix.BytePtrFromString(opts)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("kmodule.Init: could not convert %q to C string: %v", opts, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_INIT_MODULE, uintptr(unsafe.Pointer(&image[0])), uintptr(len(image)), uintptr(unsafe.Pointer(optsNull))); e != 0 {
		return &SyscallError{
			Msg:   fmt.Sprintf("init_module(%v, %q) failed", image, opts),
			Errno: e,
		}
	}

	return nil
}

// FileInit loads the kernel module contained by `f` with the given opts and
// flags.
//
// FileInit falls back to Init when the finit_module(2) syscall is not available.
func FileInit(f *os.File, opts string, flags uintptr) error {
	optsNull, err := unix.BytePtrFromString(opts)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("kmodule.Init: could not convert %q to C string: %v", opts, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_FINIT_MODULE, f.Fd(), uintptr(unsafe.Pointer(optsNull)), flags); e == unix.ENOSYS {
		if flags != 0 {
			return &SyscallError{Msg: fmt.Sprintf("finit_module unavailable"), Errno: e}
		}

		// Fall back to regular init_module(2).
		img, err := ioutil.ReadAll(f)
		if err != nil {
			return &SyscallError{Msg: fmt.Sprintf("kmodule.FileInit: %v", err)}
		}
		return Init(img, opts)
	} else if e != 0 {
		return &SyscallError{
			Msg:   fmt.Sprintf("finit_module(%v, %q, %#x) failed", f, opts, flags),
			Errno: e,
		}
	}

	return nil
}

// Delete removes a kernel module.
func Delete(name string, flags uintptr) error {
	modnameptr, err := unix.BytePtrFromString(name)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not delete module %q: %v", name, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_DELETE_MODULE, uintptr(unsafe.Pointer(modnameptr)), flags, 0); e != 0 {
		return &SyscallError{Msg: fmt.Sprintf("could not delete module %q", name), Errno: e}
	}

	return nil
}

type modState uint8

const (
	unloaded modState = iota
	loading
	loaded
)

type dependency struct {
	state modState
	deps  []string
}

type depMap map[string]*dependency

// ProbeOpts contains optional parameters to Probe.
//
// An empty ProbeOpts{} should lead to the default behavior.
type ProbeOpts struct {
	DryRun bool
}

// Probe loads the given kernel module and its dependencies.
// It is calls ProbeOptions with the default ProbeOpts.
func Probe(name string, modParams string) error {
	return ProbeOptions(name, modParams, ProbeOpts{})
}

// ProbeOptions loads the given kernel module and its dependencies.
// This functions takes ProbeOpts.
func ProbeOptions(name, modParams string, opts ProbeOpts) error {
	deps, err := genDeps()
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not generate dependency map %v", err)}
	}

	modPath, err := findModPath(name, deps)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not find module path %q: %v", name, err)}
	}

	if !opts.DryRun {
		// if the module is already loaded or does not have deps, or all of them are loaded
		// then this succeeds and we are done
		if err := loadModule(modPath, modParams, opts); err == nil {
			return nil
		}
		// okay, we have to try the hard way and load dependencies first.
	} else {
		fmt.Println("Unique dependencies in load order, already loaded ones get skipped:")
	}

	deps[modPath].state = loading
	for _, d := range deps[modPath].deps {
		if err := loadDeps(d, deps, opts); err != nil {
			return err
		}
	}
	if err := loadModule(modPath, modParams, opts); err != nil {
		return err
	}
	// we don't care to set the state to loaded
	// deps[modPath].state = loaded
	return nil
}

func genDeps() (depMap, error) {
	deps := make(depMap)

	var u unix.Utsname
	if err := unix.Uname(&u); err != nil {
		return nil, fmt.Errorf("could not get release (uname -r): %v", err)
	}
	rel := string(u.Release[:bytes.IndexByte(u.Release[:], 0)])

	moduleDirs := []string{"/lib/modules", "/usr/lib/modules"}

	var moduleDir string
	for _, moduleDirs := range(moduleDirs) {
		moduleDir = filepath.Join(moduleDirs, strings.TrimSpace(rel))
		if _, err := os.Stat(moduleDir); err == nil {
			break
		}
	}

	f, err := os.Open(filepath.Join(moduleDir, "modules.dep"))
	if err != nil {
		return nil, fmt.Errorf("could not open dependency file: %v", err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		nameDeps := strings.Split(txt, ":")
		modPath, modDeps := nameDeps[0], nameDeps[1]
		modPath = filepath.Join(moduleDir, strings.TrimSpace(modPath))

		var dependency dependency
		if len(modDeps) > 0 {
			for _, dep := range strings.Split(strings.TrimSpace(modDeps), " ") {
				dependency.deps = append(dependency.deps, filepath.Join(moduleDir, dep))
			}
		}
		deps[modPath] = &dependency
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return deps, nil
}

func findModPath(name string, m depMap) (string, error) {
	for mp := range m {
		if path.Base(mp) == name+".ko" {
			return mp, nil
		}
	}

	return "", fmt.Errorf("Could not find path for module %q", name)
}

func loadDeps(path string, m depMap, opts ProbeOpts) error {
	dependency, ok := m[path]
	if !ok {
		return &SyscallError{Msg: fmt.Sprintf("could not find dependency %q", path)}
	}

	if dependency.state == loading {
		return &SyscallError{Msg: fmt.Sprintf("circular dependency! %q already LOADING", path)}
	} else if dependency.state == loaded {
		return nil
	}

	m[path].state = loading

	for _, dep := range dependency.deps {
		if err := loadDeps(dep, m, opts); err != nil {
			return err
		}
	}

	// done with dependencies, load module
	if err := loadModule(path, "", opts); err != nil {
		return err
	}
	m[path].state = loaded

	return nil
}

func loadModule(path, modParams string, opts ProbeOpts) error {
	if opts.DryRun {
		fmt.Println(path)
		return nil
	}

	f, err := os.Open(path)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not open %q: %v", path, err)}
	}
	defer f.Close()

	if err := FileInit(f, modParams, 0); err != nil {
		if serr, ok := err.(*SyscallError); !ok || (ok && serr.Errno != unix.EEXIST) {
			return err
		}
	}

	return nil
}

<|endoftext|>


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

package network

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

	"k8s.io/apimachinery/pkg/types"

	v1 "kubevirt.io/api/core/v1"
	"kubevirt.io/kubevirt/pkg/network/cache"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
)

type NetStat struct {
	ifaceCacheFactory cache.InterfaceCacheFactory

	// In memory cache, storing pod interface information.
	// key is the file path, value is the contents.
	// if key exists, then don't read directly from file.
	podInterfaceVolatileCache PodInterfaceByVMIAndName
}

func NewNetStat(ifaceCacheFactory cache.InterfaceCacheFactory) *NetStat {
	return &NetStat{
		ifaceCacheFactory:         ifaceCacheFactory,
		podInterfaceVolatileCache: PodInterfaceByVMIAndName{},
	}
}

func (c *NetStat) Teardown(vmi *v1.VirtualMachineInstance) {
	c.podInterfaceVolatileCache.DeleteAllForVMI(vmi.UID)
}

func (c *NetStat) PodInterfaceVolatileDataIsCached(vmi *v1.VirtualMachineInstance, ifaceName string) bool {
	_, exists := c.podInterfaceVolatileCache.Load(vmi.UID, ifaceName)
	return exists
}

func (c *NetStat) CachePodInterfaceVolatileData(vmi *v1.VirtualMachineInstance, ifaceName string, data *cache.PodCacheInterface) {
	c.podInterfaceVolatileCache.Store(vmi.UID, ifaceName, data)
}

func (c *NetStat) UpdateStatus(vmi *v1.VirtualMachineInstance, domain *api.Domain) error {
	if domain == nil {
		return nil
	}

	// This is needed to be backwards compatible with vmi's which have status interfaces
	// with the name not being set
	if len(domain.Spec.Devices.Interfaces) == 0 && len(vmi.Status.Interfaces) == 1 && vmi.Status.Interfaces[0].Name == "" {
		for _, network := range vmi.Spec.Networks {
			if network.NetworkSource.Pod != nil {
				vmi.Status.Interfaces[0].Name = network.Name
			}
		}
	}

	if len(vmi.Status.Interfaces) == 0 {
		// Set Pod Interface
		interfaces := make([]v1.VirtualMachineInstanceNetworkInterface, 0)
		for _, network := range vmi.Spec.Networks {
			podIface, err := c.getPodInterfacefromFileCache(vmi, network.Name)
			if err != nil {
				return err
			}

			if podIface != nil {
				ifc := v1.VirtualMachineInstanceNetworkInterface{
					Name: network.Name,
					IP:   podIface.PodIP,
					IPs:  podIface.PodIPs,
				}
				interfaces = append(interfaces, ifc)
			}
		}
		vmi.Status.Interfaces = interfaces
	}

	if len(domain.Spec.Devices.Interfaces) > 0 || len(domain.Status.Interfaces) > 0 {
		// This calculates the vmi.Status.Interfaces based on the following data sets:
		// - vmi.Status.Interfaces - previously calculated interfaces, this can contain data (pod IP)
		//   set in the previous loops (when there are no interfaces), which can not be deleted,
		//   unless overridden by Qemu agent
		// - domain.Spec - interfaces form the Spec
		// - domain.Status.Interfaces - interfaces reported by guest agent (empty if Qemu agent not running)
		newInterfaces := []v1.VirtualMachineInstanceNetworkInterface{}

		existingInterfaceStatusByName := map[string]v1.VirtualMachineInstanceNetworkInterface{}
		for _, existingInterfaceStatus := range vmi.Status.Interfaces {
			if existingInterfaceStatus.Name != "" {
				existingInterfaceStatusByName[existingInterfaceStatus.Name] = existingInterfaceStatus
			}
		}

		domainInterfaceStatusByMac := map[string]api.InterfaceStatus{}
		for _, domainInterfaceStatus := range domain.Status.Interfaces {
			domainInterfaceStatusByMac[domainInterfaceStatus.Mac] = domainInterfaceStatus
		}

		existingInterfacesSpecByName := map[string]v1.Interface{}
		for _, existingInterfaceSpec := range vmi.Spec.Domain.Devices.Interfaces {
			existingInterfacesSpecByName[existingInterfaceSpec.Name] = existingInterfaceSpec
		}
		existingNetworksByName := map[string]v1.Network{}
		for _, existingNetwork := range vmi.Spec.Networks {
			existingNetworksByName[existingNetwork.Name] = existingNetwork
		}

		// Iterate through all domain.Spec interfaces
		for _, domainInterface := range domain.Spec.Devices.Interfaces {
			interfaceMAC := domainInterface.MAC.MAC
			var newInterface v1.VirtualMachineInstanceNetworkInterface
			var isForwardingBindingInterface = false

			if existingInterfacesSpecByName[domainInterface.Alias.GetName()].Masquerade != nil || existingInterfacesSpecByName[domainInterface.Alias.GetName()].Slirp != nil {
				isForwardingBindingInterface = true
			}

			if existingInterface, exists := existingInterfaceStatusByName[domainInterface.Alias.GetName()]; exists {
				// Reuse previously calculated interface from vmi.Status.Interfaces, updating the MAC from domain.Spec
				// Only interfaces defined in domain.Spec are handled here
				newInterface = existingInterface
				newInterface.MAC = interfaceMAC

				// If it is a Combination of Masquerade+Pod network, check IP from file cache
				if existingInterfacesSpecByName[domainInterface.Alias.GetName()].Masquerade != nil && existingNetworksByName[domainInterface.Alias.GetName()].NetworkSource.Pod != nil {
					iface, err := c.getPodInterfacefromFileCache(vmi, domainInterface.Alias.GetName())
					if err != nil {
						return err
					}

					if !reflect.DeepEqual(iface.PodIPs, existingInterfaceStatusByName[domainInterface.Alias.GetName()].IPs) {
						newInterface.Name = domainInterface.Alias.GetName()
						newInterface.IP = iface.PodIP
						newInterface.IPs = iface.PodIPs
					}
				}
			} else {
				// If not present in vmi.Status.Interfaces, create a new one based on domain.Spec
				newInterface = v1.VirtualMachineInstanceNetworkInterface{
					MAC:  interfaceMAC,
					Name: domainInterface.Alias.GetName(),
				}
			}

			// Update IP info based on information from domain.Status.Interfaces (Qemu guest)
			// Remove the interface from domainInterfaceStatusByMac to mark it as handled
			if interfaceStatus, exists := domainInterfaceStatusByMac[interfaceMAC]; exists {
				newInterface.InterfaceName = interfaceStatus.InterfaceName
				// Do not update if interface has Masquerede binding
				// virt-controller should update VMI status interface with Pod IP instead
				if !isForwardingBindingInterface {
					newInterface.IP = interfaceStatus.Ip
					newInterface.IPs = interfaceStatus.IPs
				}
				delete(domainInterfaceStatusByMac, interfaceMAC)
			}
			newInterfaces = append(newInterfaces, newInterface)
		}

		// If any of domain.Status.Interfaces were not handled above, it means that the vm contains additional
		// interfaces not defined in domain.Spec.Devices.Interfaces (most likely added by user on VM or a SRIOV interface)
		// Add them to vmi.Status.Interfaces
		setMissingSRIOVInterfacesNames(existingInterfacesSpecByName, domainInterfaceStatusByMac)
		for interfaceMAC, domainInterfaceStatus := range domainInterfaceStatusByMac {
			newInterface := v1.VirtualMachineInstanceNetworkInterface{
				Name:          domainInterfaceStatus.Name,
				MAC:           interfaceMAC,
				IP:            domainInterfaceStatus.Ip,
				IPs:           domainInterfaceStatus.IPs,
				InterfaceName: domainInterfaceStatus.InterfaceName,
			}
			newInterfaces = append(newInterfaces, newInterface)
		}
		vmi.Status.Interfaces = newInterfaces
	}
	return nil
}

func (c *NetStat) getPodInterfacefromFileCache(vmi *v1.VirtualMachineInstance, ifaceName string) (*cache.PodCacheInterface, error) {
	// Once the Interface files are set on the handler, they don't change
	// If already present in the map, don't read again
	podInterface, exists := c.podInterfaceVolatileCache.Load(vmi.UID, ifaceName)

	if exists {
		return podInterface, nil
	}

	//FIXME error handling?
	podInterface, _ = c.ifaceCacheFactory.CacheForVMI(vmi).Read(ifaceName)

	c.podInterfaceVolatileCache.Store(vmi.UID, ifaceName, podInterface)

	return podInterface, nil
}

func setMissingSRIOVInterfacesNames(interfacesSpecByName map[string]v1.Interface, interfacesStatusByMac map[string]api.InterfaceStatus) {
	for name, ifaceSpec := range interfacesSpecByName {
		if ifaceSpec.SRIOV == nil || ifaceSpec.MacAddress == "" {
			continue
		}
		if domainIfaceStatus, exists := interfacesStatusByMac[ifaceSpec.MacAddress]; exists {
			domainIfaceStatus.Name = name
			interfacesStatusByMac[ifaceSpec.MacAddress] = domainIfaceStatus
		}
	}
}

type PodInterfaceByVMIAndName struct {
	syncMap sync.Map
}

func (p *PodInterfaceByVMIAndName) DeleteAllForVMI(vmiUID types.UID) {
	// Clean Pod interface cache from map and files
	p.syncMap.Range(func(key, value interface{}) bool {
		if strings.Contains(key.(string), string(vmiUID)) {
			p.syncMap.Delete(key)
		}
		return true
	})
}

func (p *PodInterfaceByVMIAndName) Load(vmiUID types.UID, interfaceName string) (*cache.PodCacheInterface, bool) {
	result, exists := p.syncMap.Load(p.key(vmiUID, interfaceName))

	if !exists {
		return nil, false
	}
	return p.cast(result), true
}

func (p *PodInterfaceByVMIAndName) Store(vmiUID types.UID, interfaceName string, podCacheInterface *cache.PodCacheInterface) {
	p.syncMap.Store(p.key(vmiUID, interfaceName), podCacheInterface)
}

func (p *PodInterfaceByVMIAndName) Size() int {
	return syncMapLen(&p.syncMap)
}

func (*PodInterfaceByVMIAndName) cast(result interface{}) *cache.PodCacheInterface {
	podCacheInterface, ok := result.(*cache.PodCacheInterface)
	if !ok {
		panic(fmt.Sprintf("failed casting %+v to *PodCacheInterface", result))
	}
	return podCacheInterface
}

func (*PodInterfaceByVMIAndName) key(vmiUID types.UID, interfaceName string) string {
	return fmt.Sprintf("%s/%s", vmiUID, interfaceName)
}

func syncMapLen(m *sync.Map) int {
	mapLen := 0
	m.Range(func(k, v interface{}) bool {
		mapLen += 1
		return true
	})
	return mapLen
}
<commit_msg>netstat: Remove unused methods/functions.<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright 2021 Red Hat, Inc.
 *
 */

package network

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

	"k8s.io/apimachinery/pkg/types"

	v1 "kubevirt.io/api/core/v1"
	"kubevirt.io/kubevirt/pkg/network/cache"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
)

type NetStat struct {
	ifaceCacheFactory cache.InterfaceCacheFactory

	// In memory cache, storing pod interface information.
	// key is the file path, value is the contents.
	// if key exists, then don't read directly from file.
	podInterfaceVolatileCache PodInterfaceByVMIAndName
}

func NewNetStat(ifaceCacheFactory cache.InterfaceCacheFactory) *NetStat {
	return &NetStat{
		ifaceCacheFactory:         ifaceCacheFactory,
		podInterfaceVolatileCache: PodInterfaceByVMIAndName{},
	}
}

func (c *NetStat) Teardown(vmi *v1.VirtualMachineInstance) {
	c.podInterfaceVolatileCache.DeleteAllForVMI(vmi.UID)
}

func (c *NetStat) PodInterfaceVolatileDataIsCached(vmi *v1.VirtualMachineInstance, ifaceName string) bool {
	_, exists := c.podInterfaceVolatileCache.Load(vmi.UID, ifaceName)
	return exists
}

func (c *NetStat) CachePodInterfaceVolatileData(vmi *v1.VirtualMachineInstance, ifaceName string, data *cache.PodCacheInterface) {
	c.podInterfaceVolatileCache.Store(vmi.UID, ifaceName, data)
}

func (c *NetStat) UpdateStatus(vmi *v1.VirtualMachineInstance, domain *api.Domain) error {
	if domain == nil {
		return nil
	}

	// This is needed to be backwards compatible with vmi's which have status interfaces
	// with the name not being set
	if len(domain.Spec.Devices.Interfaces) == 0 && len(vmi.Status.Interfaces) == 1 && vmi.Status.Interfaces[0].Name == "" {
		for _, network := range vmi.Spec.Networks {
			if network.NetworkSource.Pod != nil {
				vmi.Status.Interfaces[0].Name = network.Name
			}
		}
	}

	if len(vmi.Status.Interfaces) == 0 {
		// Set Pod Interface
		interfaces := make([]v1.VirtualMachineInstanceNetworkInterface, 0)
		for _, network := range vmi.Spec.Networks {
			podIface, err := c.getPodInterfacefromFileCache(vmi, network.Name)
			if err != nil {
				return err
			}

			if podIface != nil {
				ifc := v1.VirtualMachineInstanceNetworkInterface{
					Name: network.Name,
					IP:   podIface.PodIP,
					IPs:  podIface.PodIPs,
				}
				interfaces = append(interfaces, ifc)
			}
		}
		vmi.Status.Interfaces = interfaces
	}

	if len(domain.Spec.Devices.Interfaces) > 0 || len(domain.Status.Interfaces) > 0 {
		// This calculates the vmi.Status.Interfaces based on the following data sets:
		// - vmi.Status.Interfaces - previously calculated interfaces, this can contain data (pod IP)
		//   set in the previous loops (when there are no interfaces), which can not be deleted,
		//   unless overridden by Qemu agent
		// - domain.Spec - interfaces form the Spec
		// - domain.Status.Interfaces - interfaces reported by guest agent (empty if Qemu agent not running)
		newInterfaces := []v1.VirtualMachineInstanceNetworkInterface{}

		existingInterfaceStatusByName := map[string]v1.VirtualMachineInstanceNetworkInterface{}
		for _, existingInterfaceStatus := range vmi.Status.Interfaces {
			if existingInterfaceStatus.Name != "" {
				existingInterfaceStatusByName[existingInterfaceStatus.Name] = existingInterfaceStatus
			}
		}

		domainInterfaceStatusByMac := map[string]api.InterfaceStatus{}
		for _, domainInterfaceStatus := range domain.Status.Interfaces {
			domainInterfaceStatusByMac[domainInterfaceStatus.Mac] = domainInterfaceStatus
		}

		existingInterfacesSpecByName := map[string]v1.Interface{}
		for _, existingInterfaceSpec := range vmi.Spec.Domain.Devices.Interfaces {
			existingInterfacesSpecByName[existingInterfaceSpec.Name] = existingInterfaceSpec
		}
		existingNetworksByName := map[string]v1.Network{}
		for _, existingNetwork := range vmi.Spec.Networks {
			existingNetworksByName[existingNetwork.Name] = existingNetwork
		}

		// Iterate through all domain.Spec interfaces
		for _, domainInterface := range domain.Spec.Devices.Interfaces {
			interfaceMAC := domainInterface.MAC.MAC
			var newInterface v1.VirtualMachineInstanceNetworkInterface
			var isForwardingBindingInterface = false

			if existingInterfacesSpecByName[domainInterface.Alias.GetName()].Masquerade != nil || existingInterfacesSpecByName[domainInterface.Alias.GetName()].Slirp != nil {
				isForwardingBindingInterface = true
			}

			if existingInterface, exists := existingInterfaceStatusByName[domainInterface.Alias.GetName()]; exists {
				// Reuse previously calculated interface from vmi.Status.Interfaces, updating the MAC from domain.Spec
				// Only interfaces defined in domain.Spec are handled here
				newInterface = existingInterface
				newInterface.MAC = interfaceMAC

				// If it is a Combination of Masquerade+Pod network, check IP from file cache
				if existingInterfacesSpecByName[domainInterface.Alias.GetName()].Masquerade != nil && existingNetworksByName[domainInterface.Alias.GetName()].NetworkSource.Pod != nil {
					iface, err := c.getPodInterfacefromFileCache(vmi, domainInterface.Alias.GetName())
					if err != nil {
						return err
					}

					if !reflect.DeepEqual(iface.PodIPs, existingInterfaceStatusByName[domainInterface.Alias.GetName()].IPs) {
						newInterface.Name = domainInterface.Alias.GetName()
						newInterface.IP = iface.PodIP
						newInterface.IPs = iface.PodIPs
					}
				}
			} else {
				// If not present in vmi.Status.Interfaces, create a new one based on domain.Spec
				newInterface = v1.VirtualMachineInstanceNetworkInterface{
					MAC:  interfaceMAC,
					Name: domainInterface.Alias.GetName(),
				}
			}

			// Update IP info based on information from domain.Status.Interfaces (Qemu guest)
			// Remove the interface from domainInterfaceStatusByMac to mark it as handled
			if interfaceStatus, exists := domainInterfaceStatusByMac[interfaceMAC]; exists {
				newInterface.InterfaceName = interfaceStatus.InterfaceName
				// Do not update if interface has Masquerede binding
				// virt-controller should update VMI status interface with Pod IP instead
				if !isForwardingBindingInterface {
					newInterface.IP = interfaceStatus.Ip
					newInterface.IPs = interfaceStatus.IPs
				}
				delete(domainInterfaceStatusByMac, interfaceMAC)
			}
			newInterfaces = append(newInterfaces, newInterface)
		}

		// If any of domain.Status.Interfaces were not handled above, it means that the vm contains additional
		// interfaces not defined in domain.Spec.Devices.Interfaces (most likely added by user on VM or a SRIOV interface)
		// Add them to vmi.Status.Interfaces
		setMissingSRIOVInterfacesNames(existingInterfacesSpecByName, domainInterfaceStatusByMac)
		for interfaceMAC, domainInterfaceStatus := range domainInterfaceStatusByMac {
			newInterface := v1.VirtualMachineInstanceNetworkInterface{
				Name:          domainInterfaceStatus.Name,
				MAC:           interfaceMAC,
				IP:            domainInterfaceStatus.Ip,
				IPs:           domainInterfaceStatus.IPs,
				InterfaceName: domainInterfaceStatus.InterfaceName,
			}
			newInterfaces = append(newInterfaces, newInterface)
		}
		vmi.Status.Interfaces = newInterfaces
	}
	return nil
}

func (c *NetStat) getPodInterfacefromFileCache(vmi *v1.VirtualMachineInstance, ifaceName string) (*cache.PodCacheInterface, error) {
	// Once the Interface files are set on the handler, they don't change
	// If already present in the map, don't read again
	podInterface, exists := c.podInterfaceVolatileCache.Load(vmi.UID, ifaceName)

	if exists {
		return podInterface, nil
	}

	//FIXME error handling?
	podInterface, _ = c.ifaceCacheFactory.CacheForVMI(vmi).Read(ifaceName)

	c.podInterfaceVolatileCache.Store(vmi.UID, ifaceName, podInterface)

	return podInterface, nil
}

func setMissingSRIOVInterfacesNames(interfacesSpecByName map[string]v1.Interface, interfacesStatusByMac map[string]api.InterfaceStatus) {
	for name, ifaceSpec := range interfacesSpecByName {
		if ifaceSpec.SRIOV == nil || ifaceSpec.MacAddress == "" {
			continue
		}
		if domainIfaceStatus, exists := interfacesStatusByMac[ifaceSpec.MacAddress]; exists {
			domainIfaceStatus.Name = name
			interfacesStatusByMac[ifaceSpec.MacAddress] = domainIfaceStatus
		}
	}
}

type PodInterfaceByVMIAndName struct {
	syncMap sync.Map
}

func (p *PodInterfaceByVMIAndName) DeleteAllForVMI(vmiUID types.UID) {
	// Clean Pod interface cache from map and files
	p.syncMap.Range(func(key, value interface{}) bool {
		if strings.Contains(key.(string), string(vmiUID)) {
			p.syncMap.Delete(key)
		}
		return true
	})
}

func (p *PodInterfaceByVMIAndName) Load(vmiUID types.UID, interfaceName string) (*cache.PodCacheInterface, bool) {
	result, exists := p.syncMap.Load(p.key(vmiUID, interfaceName))

	if !exists {
		return nil, false
	}
	return p.cast(result), true
}

func (p *PodInterfaceByVMIAndName) Store(vmiUID types.UID, interfaceName string, podCacheInterface *cache.PodCacheInterface) {
	p.syncMap.Store(p.key(vmiUID, interfaceName), podCacheInterface)
}

func (*PodInterfaceByVMIAndName) cast(result interface{}) *cache.PodCacheInterface {
	podCacheInterface, ok := result.(*cache.PodCacheInterface)
	if !ok {
		panic(fmt.Sprintf("failed casting %+v to *PodCacheInterface", result))
	}
	return podCacheInterface
}

func (*PodInterfaceByVMIAndName) key(vmiUID types.UID, interfaceName string) string {
	return fmt.Sprintf("%s/%s", vmiUID, interfaceName)
}
<|endoftext|>