<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package watch

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/davecgh/go-spew/spew"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/cache"
	"k8s.io/klog/v2"
)

// resourceVersionGetter is an interface used to get resource version from events.
// We can't reuse an interface from meta otherwise it would be a cyclic dependency and we need just this one method
type resourceVersionGetter interface {
	GetResourceVersion() string
}

// RetryWatcher will make sure that in case the underlying watcher is closed (e.g. due to API timeout or etcd timeout)
// it will get restarted from the last point without the consumer even knowing about it.
// RetryWatcher does that by inspecting events and keeping track of resourceVersion.
// Especially useful when using watch.UntilWithoutRetry where premature termination is causing issues and flakes.
// Please note that this is not resilient to etcd cache not having the resource version anymore - you would need to
// use Informers for that.
type RetryWatcher struct {
	lastResourceVersion string
	watcherClient       cache.Watcher
	resultChan          chan watch.Event
	stopChan            chan struct{}
	doneChan            chan struct{}
	minRestartDelay     time.Duration
}

// NewRetryWatcher creates a new RetryWatcher.
// It will make sure that watches gets restarted in case of recoverable errors.
// The initialResourceVersion will be given to watch method when first called.
func NewRetryWatcher(initialResourceVersion string, watcherClient cache.Watcher) (*RetryWatcher, error) {
	return newRetryWatcher(initialResourceVersion, watcherClient, 1*time.Second)
}

func newRetryWatcher(initialResourceVersion string, watcherClient cache.Watcher, minRestartDelay time.Duration) (*RetryWatcher, error) {
	switch initialResourceVersion {
	case "", "0":
		// TODO: revisit this if we ever get WATCH v2 where it means start "now"
		//       without doing the synthetic list of objects at the beginning (see #74022)
		return nil, fmt.Errorf("initial RV %q is not supported due to issues with underlying WATCH", initialResourceVersion)
	default:
		break
	}

	rw := &RetryWatcher{
		lastResourceVersion: initialResourceVersion,
		watcherClient:       watcherClient,
		stopChan:            make(chan struct{}),
		doneChan:            make(chan struct{}),
		resultChan:          make(chan watch.Event, 0),
		minRestartDelay:     minRestartDelay,
	}

	go rw.receive()
	return rw, nil
}

func (rw *RetryWatcher) send(event watch.Event) bool {
	// Writing to an unbuffered channel is blocking operation
	// and we need to check if stop wasn't requested while doing so.
	select {
	case rw.resultChan <- event:
		return true
	case <-rw.stopChan:
		return false
	}
}

// doReceive returns true when it is done, false otherwise.
// If it is not done the second return value holds the time to wait before calling it again.
func (rw *RetryWatcher) doReceive() (bool, time.Duration) {
	watcher, err := rw.watcherClient.Watch(metav1.ListOptions{
		ResourceVersion:     rw.lastResourceVersion,
		AllowWatchBookmarks: true,
	})
	// We are very unlikely to hit EOF here since we are just establishing the call,
	// but it may happen that the apiserver is just shutting down (e.g. being restarted)
	// This is consistent with how it is handled for informers
	switch err {
	case nil:
		break

	case io.EOF:
		// watch closed normally
		return false, 0

	case io.ErrUnexpectedEOF:
		klog.V(1).Infof("Watch closed with unexpected EOF: %v", err)
		return false, 0

	default:
		msg := "Watch failed: %v"
		if net.IsProbableEOF(err) || net.IsTimeout(err) {
			klog.V(5).Infof(msg, err)
			// Retry
			return false, 0
		}

		klog.Errorf(msg, err)
		// Retry
		return false, 0
	}

	if watcher == nil {
		klog.Error("Watch returned nil watcher")
		// Retry
		return false, 0
	}

	ch := watcher.ResultChan()
	defer watcher.Stop()

	for {
		select {
		case <-rw.stopChan:
			klog.V(4).Info("Stopping RetryWatcher.")
			return true, 0
		case event, ok := <-ch:
			if !ok {
				klog.V(4).Infof("Failed to get event! Re-creating the watcher. Last RV: %s", rw.lastResourceVersion)
				return false, 0
			}

			// We need to inspect the event and get ResourceVersion out of it
			switch event.Type {
			case watch.Added, watch.Modified, watch.Deleted, watch.Bookmark:
				metaObject, ok := event.Object.(resourceVersionGetter)
				if !ok {
					_ = rw.send(watch.Event{
						Type:   watch.Error,
						Object: &apierrors.NewInternalError(errors.New("retryWatcher: doesn't support resourceVersion")).ErrStatus,
					})
					// We have to abort here because this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data!
					return true, 0
				}

				resourceVersion := metaObject.GetResourceVersion()
				if resourceVersion == "" {
					_ = rw.send(watch.Event{
						Type:   watch.Error,
						Object: &apierrors.NewInternalError(fmt.Errorf("retryWatcher: object %#v doesn't support resourceVersion", event.Object)).ErrStatus,
					})
					// We have to abort here because this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data!
					return true, 0
				}

				// All is fine; send the non-bookmark events and update resource version.
				if event.Type != watch.Bookmark {
					ok = rw.send(event)
					if !ok {
						return true, 0
					}
				}
				rw.lastResourceVersion = resourceVersion

				continue

			case watch.Error:
				// This round trip allows us to handle unstructured status
				errObject := apierrors.FromObject(event.Object)
				statusErr, ok := errObject.(*apierrors.StatusError)
				if !ok {
					klog.Error(spew.Sprintf("Received an error which is not *metav1.Status but %#+v", event.Object))
					// Retry unknown errors
					return false, 0
				}

				status := statusErr.ErrStatus

				statusDelay := time.Duration(0)
				if status.Details != nil {
					statusDelay = time.Duration(status.Details.RetryAfterSeconds) * time.Second
				}

				switch status.Code {
				case http.StatusGone:
					// Never retry RV too old errors
					_ = rw.send(event)
					return true, 0

				case http.StatusGatewayTimeout, http.StatusInternalServerError:
					// Retry
					return false, statusDelay

				default:
					// We retry by default. RetryWatcher is meant to proceed unless it is certain
					// that it can't. If we are not certain, we proceed with retry and leave it
					// up to the user to timeout if needed.

					// Log here so we have a record of hitting the unexpected error
					// and we can whitelist some error codes if we missed any that are expected.
					klog.V(5).Info(spew.Sprintf("Retrying after unexpected error: %#+v", event.Object))

					// Retry
					return false, statusDelay
				}

			default:
				klog.Errorf("Failed to recognize Event type %q", event.Type)
				_ = rw.send(watch.Event{
					Type:   watch.Error,
					Object: &apierrors.NewInternalError(fmt.Errorf("retryWatcher failed to recognize Event type %q", event.Type)).ErrStatus,
				})
				// We are unable to restart the watch and have to stop the loop or this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data!
				return true, 0
			}
		}
	}
}

// receive reads the result from a watcher, restarting it if necessary.
func (rw *RetryWatcher) receive() {
	defer close(rw.doneChan)
	defer close(rw.resultChan)

	klog.V(4).Info("Starting RetryWatcher.")
	defer klog.V(4).Info("Stopping RetryWatcher.")

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go func() {
		select {
		case <-rw.stopChan:
			cancel()
			return
		case <-ctx.Done():
			return
		}
	}()

	// We use non sliding until so we don't introduce delays on happy path when WATCH call
	// timeouts or gets closed and we need to reestablish it while also avoiding hot loops.
	wait.NonSlidingUntilWithContext(ctx, func(ctx context.Context) {
		done, retryAfter := rw.doReceive()
		if done {
			cancel()
			return
		}

		time.Sleep(retryAfter)

		klog.V(4).Infof("Restarting RetryWatcher at RV=%q", rw.lastResourceVersion)
	}, rw.minRestartDelay)
}

// ResultChan implements Interface.
func (rw *RetryWatcher) ResultChan() <-chan watch.Event {
	return rw.resultChan
}

// Stop implements Interface.
func (rw *RetryWatcher) Stop() {
	close(rw.stopChan)
}

// Done allows the caller to be notified when Retry watcher stops.
func (rw *RetryWatcher) Done() <-chan struct{} {
	return rw.doneChan
}
<commit_msg>Migrate client-go retry-watcher to structured logging<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package watch

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/davecgh/go-spew/spew"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/cache"
	"k8s.io/klog/v2"
)

// resourceVersionGetter is an interface used to get resource version from events.
// We can't reuse an interface from meta otherwise it would be a cyclic dependency and we need just this one method
type resourceVersionGetter interface {
	GetResourceVersion() string
}

// RetryWatcher will make sure that in case the underlying watcher is closed (e.g. due to API timeout or etcd timeout)
// it will get restarted from the last point without the consumer even knowing about it.
// RetryWatcher does that by inspecting events and keeping track of resourceVersion.
// Especially useful when using watch.UntilWithoutRetry where premature termination is causing issues and flakes.
// Please note that this is not resilient to etcd cache not having the resource version anymore - you would need to
// use Informers for that.
type RetryWatcher struct {
	lastResourceVersion string
	watcherClient       cache.Watcher
	resultChan          chan watch.Event
	stopChan            chan struct{}
	doneChan            chan struct{}
	minRestartDelay     time.Duration
}

// NewRetryWatcher creates a new RetryWatcher.
// It will make sure that watches gets restarted in case of recoverable errors.
// The initialResourceVersion will be given to watch method when first called.
func NewRetryWatcher(initialResourceVersion string, watcherClient cache.Watcher) (*RetryWatcher, error) {
	return newRetryWatcher(initialResourceVersion, watcherClient, 1*time.Second)
}

func newRetryWatcher(initialResourceVersion string, watcherClient cache.Watcher, minRestartDelay time.Duration) (*RetryWatcher, error) {
	switch initialResourceVersion {
	case "", "0":
		// TODO: revisit this if we ever get WATCH v2 where it means start "now"
		//       without doing the synthetic list of objects at the beginning (see #74022)
		return nil, fmt.Errorf("initial RV %q is not supported due to issues with underlying WATCH", initialResourceVersion)
	default:
		break
	}

	rw := &RetryWatcher{
		lastResourceVersion: initialResourceVersion,
		watcherClient:       watcherClient,
		stopChan:            make(chan struct{}),
		doneChan:            make(chan struct{}),
		resultChan:          make(chan watch.Event, 0),
		minRestartDelay:     minRestartDelay,
	}

	go rw.receive()
	return rw, nil
}

func (rw *RetryWatcher) send(event watch.Event) bool {
	// Writing to an unbuffered channel is blocking operation
	// and we need to check if stop wasn't requested while doing so.
	select {
	case rw.resultChan <- event:
		return true
	case <-rw.stopChan:
		return false
	}
}

// doReceive returns true when it is done, false otherwise.
// If it is not done the second return value holds the time to wait before calling it again.
func (rw *RetryWatcher) doReceive() (bool, time.Duration) {
	watcher, err := rw.watcherClient.Watch(metav1.ListOptions{
		ResourceVersion:     rw.lastResourceVersion,
		AllowWatchBookmarks: true,
	})
	// We are very unlikely to hit EOF here since we are just establishing the call,
	// but it may happen that the apiserver is just shutting down (e.g. being restarted)
	// This is consistent with how it is handled for informers
	switch err {
	case nil:
		break

	case io.EOF:
		// watch closed normally
		return false, 0

	case io.ErrUnexpectedEOF:
		klog.V(1).InfoS("Watch closed with unexpected EOF", "err", err)
		return false, 0

	default:
		msg := "Watch failed"
		if net.IsProbableEOF(err) || net.IsTimeout(err) {
			klog.V(5).InfoS(msg, "err", err)
			// Retry
			return false, 0
		}

		klog.ErrorS(err, msg)
		// Retry
		return false, 0
	}

	if watcher == nil {
		klog.ErrorS(nil, "Watch returned nil watcher")
		// Retry
		return false, 0
	}

	ch := watcher.ResultChan()
	defer watcher.Stop()

	for {
		select {
		case <-rw.stopChan:
			klog.V(4).InfoS("Stopping RetryWatcher.")
			return true, 0
		case event, ok := <-ch:
			if !ok {
				klog.V(4).InfoS("Failed to get event! Re-creating the watcher.", "resourceVersion", rw.lastResourceVersion)
				return false, 0
			}

			// We need to inspect the event and get ResourceVersion out of it
			switch event.Type {
			case watch.Added, watch.Modified, watch.Deleted, watch.Bookmark:
				metaObject, ok := event.Object.(resourceVersionGetter)
				if !ok {
					_ = rw.send(watch.Event{
						Type:   watch.Error,
						Object: &apierrors.NewInternalError(errors.New("retryWatcher: doesn't support resourceVersion")).ErrStatus,
					})
					// We have to abort here because this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data!
					return true, 0
				}

				resourceVersion := metaObject.GetResourceVersion()
				if resourceVersion == "" {
					_ = rw.send(watch.Event{
						Type:   watch.Error,
						Object: &apierrors.NewInternalError(fmt.Errorf("retryWatcher: object %#v doesn't support resourceVersion", event.Object)).ErrStatus,
					})
					// We have to abort here because this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data!
					return true, 0
				}

				// All is fine; send the non-bookmark events and update resource version.
				if event.Type != watch.Bookmark {
					ok = rw.send(event)
					if !ok {
						return true, 0
					}
				}
				rw.lastResourceVersion = resourceVersion

				continue

			case watch.Error:
				// This round trip allows us to handle unstructured status
				errObject := apierrors.FromObject(event.Object)
				statusErr, ok := errObject.(*apierrors.StatusError)
				if !ok {
					klog.Error(spew.Sprintf("Received an error which is not *metav1.Status but %#+v", event.Object))
					// Retry unknown errors
					return false, 0
				}

				status := statusErr.ErrStatus

				statusDelay := time.Duration(0)
				if status.Details != nil {
					statusDelay = time.Duration(status.Details.RetryAfterSeconds) * time.Second
				}

				switch status.Code {
				case http.StatusGone:
					// Never retry RV too old errors
					_ = rw.send(event)
					return true, 0

				case http.StatusGatewayTimeout, http.StatusInternalServerError:
					// Retry
					return false, statusDelay

				default:
					// We retry by default. RetryWatcher is meant to proceed unless it is certain
					// that it can't. If we are not certain, we proceed with retry and leave it
					// up to the user to timeout if needed.

					// Log here so we have a record of hitting the unexpected error
					// and we can whitelist some error codes if we missed any that are expected.
					klog.V(5).Info(spew.Sprintf("Retrying after unexpected error: %#+v", event.Object))

					// Retry
					return false, statusDelay
				}

			default:
				klog.Errorf("Failed to recognize Event type %q", event.Type)
				_ = rw.send(watch.Event{
					Type:   watch.Error,
					Object: &apierrors.NewInternalError(fmt.Errorf("retryWatcher failed to recognize Event type %q", event.Type)).ErrStatus,
				})
				// We are unable to restart the watch and have to stop the loop or this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data!
				return true, 0
			}
		}
	}
}

// receive reads the result from a watcher, restarting it if necessary.
func (rw *RetryWatcher) receive() {
	defer close(rw.doneChan)
	defer close(rw.resultChan)

	klog.V(4).Info("Starting RetryWatcher.")
	defer klog.V(4).Info("Stopping RetryWatcher.")

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go func() {
		select {
		case <-rw.stopChan:
			cancel()
			return
		case <-ctx.Done():
			return
		}
	}()

	// We use non sliding until so we don't introduce delays on happy path when WATCH call
	// timeouts or gets closed and we need to reestablish it while also avoiding hot loops.
	wait.NonSlidingUntilWithContext(ctx, func(ctx context.Context) {
		done, retryAfter := rw.doReceive()
		if done {
			cancel()
			return
		}

		time.Sleep(retryAfter)

		klog.V(4).Infof("Restarting RetryWatcher at RV=%q", rw.lastResourceVersion)
	}, rw.minRestartDelay)
}

// ResultChan implements Interface.
func (rw *RetryWatcher) ResultChan() <-chan watch.Event {
	return rw.resultChan
}

// Stop implements Interface.
func (rw *RetryWatcher) Stop() {
	close(rw.stopChan)
}

// Done allows the caller to be notified when Retry watcher stops.
func (rw *RetryWatcher) Done() <-chan struct{} {
	return rw.doneChan
}
<|endoftext|>


<commit_before>// (C) Copyright 2018 Hewlett Packard Enterprise Development LP
//
// 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 oneview
import (
	"github.com/HewlettPackard/oneview-golang/ov"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/hashicorp/terraform/helper/schema"
)
func resourceEnclosureGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceEnclosureGroupCreate,
		Read:   resourceEnclosureGroupRead,
		Update: resourceEnclosureGroupUpdate,
		Delete: resourceEnclosureGroupDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			"ambient_temperature_mode": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"associated_logical_interconnect_groups": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"category": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"etag": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"enclosure_count": {
				Type:     schema.TypeInt,
				Optional: true,
			},
			"enclosure_type_uri": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"initial_scope_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"interconnect_bay_mapping_count": {
				Type:     schema.TypeInt,
				Optional: true,
			},
			"interconnect_bay_mappings": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"enclosure_index": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"interconnect_bay": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"logical_interconnect_group_uri": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"ip_addressing_mode": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"ip_range_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"port_mapping_count": {
				Type:     schema.TypeInt,
				Optional: true,
			},
			"port_mappings": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"interconnect_bay": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"mid_plane_port": {
							Type:     schema.TypeInt,
							Optional: true,
						},
					},
				},
			},
			"power_mode": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"scopes_uri": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"stacking_mode": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"Status": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"type": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}
func resourceEnclosureGroupCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	enclosureGroup := ov.EnclosureGroup{
		Name: d.Get("name").(string),
	}
	if val, ok := d.GetOk("interconnect_bay_mappings"); ok {
		rawInterconnectBayMappings := val.(*schema.Set).List()
		interconnectBayMappings := make([]ov.InterconnectBayMap, 0)
		for _, raw := range rawInterconnectBayMappings {
			interconnectBayMappingItem := raw.(map[string]interface{})
			interconnectBayMappings = append(interconnectBayMappings, ov.InterconnectBayMap{
				EnclosureIndex:              interconnectBayMappingItem["enclosure_index"].(int),
				InterconnectBay:             interconnectBayMappingItem["interconnect_bay"].(int),
				LogicalInterconnectGroupUri: utils.NewNstring(interconnectBayMappingItem["logical_interconnect_group_uri"].(string))})
		}
		enclosureGroup.InterconnectBayMappings = interconnectBayMappings
	}
	if val, ok := d.GetOk("initial_scope_uris"); ok {
		rawinitialScopeUris := val.(*schema.Set).List()
		initialScopeUris := make([]utils.Nstring, len(rawinitialScopeUris))
		for i, rawData := range rawinitialScopeUris {
			initialScopeUris[i] = utils.Nstring(rawData.(string))
		}
		enclosureGroup.InitialScopeUris = initialScopeUris
	}
	if val, ok := d.GetOk("ambient_temperature_mode"); ok {
		enclosureGroup.AmbientTemperatureMode = val.(string)
	}
	if val, ok := d.GetOk("ambient_temperature_mode"); ok {
		enclosureGroup.AmbientTemperatureMode = val.(string)
	}
	if val, ok := d.GetOk("enclosure_count"); ok {
		enclosureGroup.EnclosureCount = val.(int)
	}
	if val, ok := d.GetOk("power_mode"); ok {
		enclosureGroup.PowerMode = val.(string)
	}
	if val, ok := d.GetOk("ip_addressing_mode"); ok {
		enclosureGroup.IpAddressingMode = val.(string)
	}
	if val, ok := d.GetOk("ip_range_uris"); ok {
		rawIpRangeUris := val.(*schema.Set).List()
		ipRangeUris := make([]utils.Nstring, 0)
		for _, rawData := range rawIpRangeUris {
			ipRangeUris = append(ipRangeUris, utils.Nstring(rawData.(string)))
		}
		enclosureGroup.IpRangeUris = ipRangeUris
	}
	enclosureGroupError := config.ovClient.CreateEnclosureGroup(enclosureGroup)
	d.SetId(d.Get("name").(string))
	if enclosureGroupError != nil {
		d.SetId("")
		return enclosureGroupError
	}
	return resourceEnclosureGroupRead(d, meta)
}
func resourceEnclosureGroupRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	enclosureGroup, err := config.ovClient.GetEnclosureGroupByName(d.Id())
	if err != nil || enclosureGroup.URI.IsNil() {
		d.SetId("")
		return nil
	}
	d.Set("ambient_temperature_mode", enclosureGroup.AmbientTemperatureMode)
	d.Set("associated_logical_interconnect_groups", enclosureGroup.AssociatedLogicalInterconnectGroups)
	d.Set("category", enclosureGroup.Category)
	d.Set("description", enclosureGroup.Description)
	d.Set("eTag", enclosureGroup.ETAG)
	d.Set("enclosure_count", enclosureGroup.EnclosureCount)
	d.Set("enclosure_type_uri", enclosureGroup.EnclosureTypeUri.String())
	d.Set("initial_scope_uris", enclosureGroup.InitialScopeUris)
	d.Set("interconnect_bay_mapping_count", enclosureGroup.InterconnectBayMappingCount)
	d.Set("interconnect_bay_mappings", enclosureGroup.InterconnectBayMappings)
	d.Set("ip_addressing_mode", enclosureGroup.IpAddressingMode)
	d.Set("ip_range_uris", enclosureGroup.IpRangeUris)
	d.Set("name", enclosureGroup.Name)
	d.Set("os_deployment_settings", enclosureGroup.OsDeploymentSettings)
	d.Set("port_mapping_count", enclosureGroup.PortMappingCount)
	d.Set("port_mappings", enclosureGroup.PortMappings)
	d.Set("power_mode", enclosureGroup.PowerMode)
	d.Set("scopes_uri", enclosureGroup.ScopesUri.String())
	d.Set("stacking_mode", enclosureGroup.StackingMode)
	d.Set("status", enclosureGroup.Status)
	d.Set("type", enclosureGroup.Type)
	d.Set("uri", enclosureGroup.URI.String())
	return nil
}
func resourceEnclosureGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	enclosureGroup := ov.EnclosureGroup{
		URI:                         utils.NewNstring(d.Get("uri").(string)),
		InterconnectBayMappingCount: d.Get("interconnect_bay_mapping_count").(int),
		Type:                        d.Get("type").(string),
		StackingMode:                d.Get("stacking_mode").(string),
	}

	rawInterconnectBayMappings := d.Get("interconnect_bay_mappings").(*schema.Set).List()
	interconnectBayMappings := make([]ov.InterconnectBayMap, 0)
	for _, raw := range rawInterconnectBayMappings {
		interconnectBayMappingItem := raw.(map[string]interface{})

		interconnectBayMappings = append(interconnectBayMappings, ov.InterconnectBayMap{
			EnclosureIndex:              interconnectBayMappingItem["enclosure_index"].(int),
			InterconnectBay:             interconnectBayMappingItem["interconnect_bay"].(int),
			LogicalInterconnectGroupUri: utils.NewNstring(interconnectBayMappingItem["logical_interconnect_group_uri"].(string))})
	}
	enclosureGroup.InterconnectBayMappings = interconnectBayMappings

	// Optional Parameters
	if val, ok := d.GetOk("ambient_temperature_mode"); ok {
		enclosureGroup.AmbientTemperatureMode = val.(string)
	}

	if val, ok := d.GetOk("associated_logical_interconnect_groups"); ok {
		rawData := val.(*schema.Set).List()
		associatedLIG := make([]string, 0)
		for _, rawDataItem := range rawData {
			associatedLIG = append(associatedLIG, rawDataItem.(string))
		}
		enclosureGroup.AssociatedLogicalInterconnectGroups = associatedLIG
	}

	if val, ok := d.GetOk("category"); ok {
		enclosureGroup.Category = val.(string)
	}

	if val, ok := d.GetOk("description"); ok {
		enclosureGroup.Description = utils.NewNstring(val.(string))
	}

	if val, ok := d.GetOk("etag"); ok {
		enclosureGroup.ETAG = val.(string)
	}

	if val, ok := d.GetOk("enclosure_count"); ok {
		enclosureGroup.EnclosureCount = val.(int)
	}

	if val, ok := d.GetOk("enclosure_type_uri"); ok {
		enclosureGroup.EnclosureTypeUri = utils.NewNstring(val.(string))
	}

	if val, ok := d.GetOk("ip_addressing_mode"); ok {
		enclosureGroup.IpAddressingMode = val.(string)
	}

	if val, ok := d.GetOk("initial_scope_uris"); ok {
		rawinitialScopeUris := val.(*schema.Set).List()
		initialScopeUris := make([]utils.Nstring, 0)
		for _, rawData := range rawinitialScopeUris {
			initialScopeUris = append(initialScopeUris, utils.Nstring(rawData.(string)))
		}
		enclosureGroup.InitialScopeUris = initialScopeUris
	}

	if val, ok := d.GetOk("name"); ok {
		enclosureGroup.Name = val.(string)
	}

	if val, ok := d.GetOk("port_mapping_count"); ok {
		enclosureGroup.PortMappingCount = val.(int)
	}

	if val, ok := d.GetOk("port_mappings"); ok {
		rawPortMappings := val.(*schema.Set).List()
		portMappings := make([]ov.PortMap, 0)
		for _, raw := range rawPortMappings {
			portMappingItem := raw.(map[string]interface{})

			portMappings = append(portMappings, ov.PortMap{
				InterconnectBay: portMappingItem["interconnect_bay"].(int),
				MidplanePort:    portMappingItem["mid_plane_port"].(int)})
		}
		enclosureGroup.PortMappings = portMappings
	}

	if val, ok := d.GetOk("power_mode"); ok {
		enclosureGroup.PowerMode = val.(string)
	}

	if val, ok := d.GetOk("scopes_uri"); ok {
		enclosureGroup.ScopesUri = utils.NewNstring(val.(string))
	}

	err := config.ovClient.UpdateEnclosureGroup(enclosureGroup)
	if err != nil {
		return err
	}
	d.SetId(d.Get("name").(string))

	return resourceEnclosureGroupRead(d, meta)
}

func resourceEnclosureGroupDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	err := config.ovClient.DeleteEnclosureGroup(d.Get("name").(string))
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Debugging go fmt check<commit_after>// (C) Copyright 2018 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"github.com/HewlettPackard/oneview-golang/ov"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceEnclosureGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceEnclosureGroupCreate,
		Read:   resourceEnclosureGroupRead,
		Update: resourceEnclosureGroupUpdate,
		Delete: resourceEnclosureGroupDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			"ambient_temperature_mode": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"associated_logical_interconnect_groups": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"category": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"etag": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"enclosure_count": {
				Type:     schema.TypeInt,
				Optional: true,
			},
			"enclosure_type_uri": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"initial_scope_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"interconnect_bay_mapping_count": {
				Type:     schema.TypeInt,
				Optional: true,
			},
			"interconnect_bay_mappings": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"enclosure_index": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"interconnect_bay": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"logical_interconnect_group_uri": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"ip_addressing_mode": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"ip_range_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"port_mapping_count": {
				Type:     schema.TypeInt,
				Optional: true,
			},
			"port_mappings": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"interconnect_bay": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"mid_plane_port": {
							Type:     schema.TypeInt,
							Optional: true,
						},
					},
				},
			},
			"power_mode": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"scopes_uri": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"stacking_mode": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"Status": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"type": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}
func resourceEnclosureGroupCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	enclosureGroup := ov.EnclosureGroup{
		Name: d.Get("name").(string),
	}
	if val, ok := d.GetOk("interconnect_bay_mappings"); ok {
		rawInterconnectBayMappings := val.(*schema.Set).List()
		interconnectBayMappings := make([]ov.InterconnectBayMap, 0)
		for _, raw := range rawInterconnectBayMappings {
			interconnectBayMappingItem := raw.(map[string]interface{})
			interconnectBayMappings = append(interconnectBayMappings, ov.InterconnectBayMap{
				EnclosureIndex:              interconnectBayMappingItem["enclosure_index"].(int),
				InterconnectBay:             interconnectBayMappingItem["interconnect_bay"].(int),
				LogicalInterconnectGroupUri: utils.NewNstring(interconnectBayMappingItem["logical_interconnect_group_uri"].(string))})
		}
		enclosureGroup.InterconnectBayMappings = interconnectBayMappings
	}
	if val, ok := d.GetOk("initial_scope_uris"); ok {
		rawinitialScopeUris := val.(*schema.Set).List()
		initialScopeUris := make([]utils.Nstring, len(rawinitialScopeUris))
		for i, rawData := range rawinitialScopeUris {
			initialScopeUris[i] = utils.Nstring(rawData.(string))
		}
		enclosureGroup.InitialScopeUris = initialScopeUris
	}
	if val, ok := d.GetOk("ambient_temperature_mode"); ok {
		enclosureGroup.AmbientTemperatureMode = val.(string)
	}
	if val, ok := d.GetOk("ambient_temperature_mode"); ok {
		enclosureGroup.AmbientTemperatureMode = val.(string)
	}
	if val, ok := d.GetOk("enclosure_count"); ok {
		enclosureGroup.EnclosureCount = val.(int)
	}
	if val, ok := d.GetOk("power_mode"); ok {
		enclosureGroup.PowerMode = val.(string)
	}
	if val, ok := d.GetOk("ip_addressing_mode"); ok {
		enclosureGroup.IpAddressingMode = val.(string)
	}
	if val, ok := d.GetOk("ip_range_uris"); ok {
		rawIpRangeUris := val.(*schema.Set).List()
		ipRangeUris := make([]utils.Nstring, 0)
		for _, rawData := range rawIpRangeUris {
			ipRangeUris = append(ipRangeUris, utils.Nstring(rawData.(string)))
		}
		enclosureGroup.IpRangeUris = ipRangeUris
	}
	enclosureGroupError := config.ovClient.CreateEnclosureGroup(enclosureGroup)
	d.SetId(d.Get("name").(string))
	if enclosureGroupError != nil {
		d.SetId("")
		return enclosureGroupError
	}
	return resourceEnclosureGroupRead(d, meta)
}
func resourceEnclosureGroupRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	enclosureGroup, err := config.ovClient.GetEnclosureGroupByName(d.Id())
	if err != nil || enclosureGroup.URI.IsNil() {
		d.SetId("")
		return nil
	}
	d.Set("ambient_temperature_mode", enclosureGroup.AmbientTemperatureMode)
	d.Set("associated_logical_interconnect_groups", enclosureGroup.AssociatedLogicalInterconnectGroups)
	d.Set("category", enclosureGroup.Category)
	d.Set("description", enclosureGroup.Description)
	d.Set("eTag", enclosureGroup.ETAG)
	d.Set("enclosure_count", enclosureGroup.EnclosureCount)
	d.Set("enclosure_type_uri", enclosureGroup.EnclosureTypeUri.String())
	d.Set("initial_scope_uris", enclosureGroup.InitialScopeUris)
	d.Set("interconnect_bay_mapping_count", enclosureGroup.InterconnectBayMappingCount)
	d.Set("interconnect_bay_mappings", enclosureGroup.InterconnectBayMappings)
	d.Set("ip_addressing_mode", enclosureGroup.IpAddressingMode)
	d.Set("ip_range_uris", enclosureGroup.IpRangeUris)
	d.Set("name", enclosureGroup.Name)
	d.Set("os_deployment_settings", enclosureGroup.OsDeploymentSettings)
	d.Set("port_mapping_count", enclosureGroup.PortMappingCount)
	d.Set("port_mappings", enclosureGroup.PortMappings)
	d.Set("power_mode", enclosureGroup.PowerMode)
	d.Set("scopes_uri", enclosureGroup.ScopesUri.String())
	d.Set("stacking_mode", enclosureGroup.StackingMode)
	d.Set("status", enclosureGroup.Status)
	d.Set("type", enclosureGroup.Type)
	d.Set("uri", enclosureGroup.URI.String())
	return nil
}
func resourceEnclosureGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	enclosureGroup := ov.EnclosureGroup{
		URI:                         utils.NewNstring(d.Get("uri").(string)),
		InterconnectBayMappingCount: d.Get("interconnect_bay_mapping_count").(int),
		Type:                        d.Get("type").(string),
		StackingMode:                d.Get("stacking_mode").(string),
	}

	rawInterconnectBayMappings := d.Get("interconnect_bay_mappings").(*schema.Set).List()
	interconnectBayMappings := make([]ov.InterconnectBayMap, 0)
	for _, raw := range rawInterconnectBayMappings {
		interconnectBayMappingItem := raw.(map[string]interface{})

		interconnectBayMappings = append(interconnectBayMappings, ov.InterconnectBayMap{
			EnclosureIndex:              interconnectBayMappingItem["enclosure_index"].(int),
			InterconnectBay:             interconnectBayMappingItem["interconnect_bay"].(int),
			LogicalInterconnectGroupUri: utils.NewNstring(interconnectBayMappingItem["logical_interconnect_group_uri"].(string))})
	}
	enclosureGroup.InterconnectBayMappings = interconnectBayMappings

	// Optional Parameters
	if val, ok := d.GetOk("ambient_temperature_mode"); ok {
		enclosureGroup.AmbientTemperatureMode = val.(string)
	}

	if val, ok := d.GetOk("associated_logical_interconnect_groups"); ok {
		rawData := val.(*schema.Set).List()
		associatedLIG := make([]string, 0)
		for _, rawDataItem := range rawData {
			associatedLIG = append(associatedLIG, rawDataItem.(string))
		}
		enclosureGroup.AssociatedLogicalInterconnectGroups = associatedLIG
	}

	if val, ok := d.GetOk("category"); ok {
		enclosureGroup.Category = val.(string)
	}

	if val, ok := d.GetOk("description"); ok {
		enclosureGroup.Description = utils.NewNstring(val.(string))
	}

	if val, ok := d.GetOk("etag"); ok {
		enclosureGroup.ETAG = val.(string)
	}

	if val, ok := d.GetOk("enclosure_count"); ok {
		enclosureGroup.EnclosureCount = val.(int)
	}

	if val, ok := d.GetOk("enclosure_type_uri"); ok {
		enclosureGroup.EnclosureTypeUri = utils.NewNstring(val.(string))
	}

	if val, ok := d.GetOk("ip_addressing_mode"); ok {
		enclosureGroup.IpAddressingMode = val.(string)
	}

	if val, ok := d.GetOk("initial_scope_uris"); ok {
		rawinitialScopeUris := val.(*schema.Set).List()
		initialScopeUris := make([]utils.Nstring, 0)
		for _, rawData := range rawinitialScopeUris {
			initialScopeUris = append(initialScopeUris, utils.Nstring(rawData.(string)))
		}
		enclosureGroup.InitialScopeUris = initialScopeUris
	}

	if val, ok := d.GetOk("name"); ok {
		enclosureGroup.Name = val.(string)
	}

	if val, ok := d.GetOk("port_mapping_count"); ok {
		enclosureGroup.PortMappingCount = val.(int)
	}

	if val, ok := d.GetOk("port_mappings"); ok {
		rawPortMappings := val.(*schema.Set).List()
		portMappings := make([]ov.PortMap, 0)
		for _, raw := range rawPortMappings {
			portMappingItem := raw.(map[string]interface{})

			portMappings = append(portMappings, ov.PortMap{
				InterconnectBay: portMappingItem["interconnect_bay"].(int),
				MidplanePort:    portMappingItem["mid_plane_port"].(int)})
		}
		enclosureGroup.PortMappings = portMappings
	}

	if val, ok := d.GetOk("power_mode"); ok {
		enclosureGroup.PowerMode = val.(string)
	}

	if val, ok := d.GetOk("scopes_uri"); ok {
		enclosureGroup.ScopesUri = utils.NewNstring(val.(string))
	}

	err := config.ovClient.UpdateEnclosureGroup(enclosureGroup)
	if err != nil {
		return err
	}
	d.SetId(d.Get("name").(string))

	return resourceEnclosureGroupRead(d, meta)
}

func resourceEnclosureGroupDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	err := config.ovClient.DeleteEnclosureGroup(d.Get("name").(string))
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package controller

import (
	. "eaciit/wfdemo-git/library/core"
	. "eaciit/wfdemo-git/library/models"
	"strings"
	"time"

	"eaciit/wfdemo-git/web/helper"

	knot "github.com/eaciit/knot/knot.v1"
	tk "github.com/eaciit/toolkit"
)

type MonitoringController struct {
	App
}

func CreateMonitoringController() *MonitoringController {
	var controller = new(MonitoringController)
	return controller
}

func (m *MonitoringController) GetData(k *knot.WebContext) interface{} {
	k.Config.OutputType = knot.OutputJson

	p := tk.M{}
	e := k.GetPayload(&p)

	if e != nil {
		return helper.CreateResult(false, nil, e.Error())
	}

	// get the last data for monitoring

	turbine := p.Get("turbine").([]interface{})
	project := ""
	if p.GetString("project") != "" {
		anProject := strings.Split(p.GetString("project"), "(")
		project = strings.TrimRight(anProject[0], " ")
	}

	match := tk.M{}
	turbines := map[string]tk.M{}
	var projectList []interface{}
	if project != "" {
		match.Set("project", project)
		projectList = append(projectList, project)
	}

	turbines, _, _ = helper.GetProjectTurbineList(projectList)

	if len(turbine) > 0 {
		match.Set("turbine", tk.M{}.Set("$in", turbine))
	}

	group := tk.M{
		"_id": tk.M{
			"project": "$project",
			"turbine": "$turbine",
		},
		"timestamp":     tk.M{"$last": "$timestamp"},
		"windspeed":     tk.M{"$last": "$windspeed"},
		"production":    tk.M{"$last": "$production"},
		"rotorspeedrpm": tk.M{"$last": "$rotorspeedrpm"},
		"status":        tk.M{"$last": "$status"},
		"statuscode":    tk.M{"$last": "$statuscode"},
		"statusdesc":    tk.M{"$last": "$statusdesc"},
	}

	var pipes []tk.M
	pipes = append(pipes, tk.M{}.Set("$match", match))
	pipes = append(pipes, tk.M{}.Set("$group", group))
	pipes = append(pipes, tk.M{}.Set("$sort", tk.M{
		"_id.project": 1,
		"_id.turbine": 1,
	}))

	csr, e := DB().Connection.NewQuery().
		From(new(Monitoring).TableName()).
		Command("pipe", pipes).
		Cursor(nil)

	defer csr.Close()

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	results := make([]tk.M, 0)
	e = csr.Fetch(&results, 0, false)

	projects := tk.M{}

	for _, v := range results {
		ID := v.Get("_id").(tk.M)
		project := ID.GetString("project")
		turbine := ID.GetString("turbine")
		timestamp := v.Get("timestamp").(time.Time).UTC()
		windspeed := checkNAValue(v.GetFloat64("windspeed"))
		production := checkNAValue(v.GetFloat64("production"))
		rotorspeedrpm := checkNAValue(v.GetFloat64("rotorspeedrpm"))
		status := v.GetString("status")
		statuscode := v.GetString("statuscode")
		statusdesc := v.GetString("statusdesc")

		list := []tk.M{}
		newRecord := tk.M{}

		updated := tk.M{}
		capacitymw := 0.0

		if projects.Get(project) != nil {
			if projects.Get(project).(tk.M).Get("turbines") != nil {
				list = projects.Get(project).(tk.M).Get("turbines").([]tk.M)
			}
			updated = projects.Get(project).(tk.M)
		}

		if turbines[project].Get(turbine) != nil {
			capacitymw = turbines[project].Get(turbine).(TurbineMaster).CapacityMW
		}

		newRecord.Set("turbine", turbine)
		newRecord.Set("timestamp", timestamp)
		newRecord.Set("timestampstr", timestamp.Format("02-01-2006 15:04:05"))
		newRecord.Set("windspeed", windspeed)
		newRecord.Set("production", production)
		newRecord.Set("rotorspeedrpm", rotorspeedrpm)
		newRecord.Set("status", status)
		newRecord.Set("statuscode", statuscode)
		newRecord.Set("statusdesc", statusdesc)

		list = append(list, newRecord)
		updated.Set("turbines", list)
		updated.Set("totalturbines", updated.GetInt("totalturbines")+1)
		updated.Set("totalcap", updated.GetFloat64("totalcap")+capacitymw)
		updated.Set("totalprod", updated.GetFloat64("totalprod")+production)

		projects.Set(project, updated)
	}

	// get today total production

	match = tk.M{}
	if project != "" {
		match.Set("project", project)
	}

	if len(turbine) > 0 {
		match.Set("turbine", tk.M{}.Set("$in", turbine))
	}

	dateid, _ := time.Parse("20060102_0504", time.Now().Format("20060102_")+"_0000")
	match.Set("dateinfo.dateid", dateid.UTC())

	group = tk.M{
		"_id":        "$project",
		"production": tk.M{"$sum": "$production"},
	}

	pipes = []tk.M{}
	pipes = append(pipes, tk.M{}.Set("$match", match))
	pipes = append(pipes, tk.M{}.Set("$group", group))

	csr, e = DB().Connection.NewQuery().
		From(new(Monitoring).TableName()).
		Command("pipe", pipes).
		Cursor(nil)

	defer csr.Close()

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	results = make([]tk.M, 0)
	e = csr.Fetch(&results, 0, false)

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	projectProdResult := map[string]float64{}

	for _, v := range results {
		id := v.GetString("_id")
		prod := v.GetFloat64("production")
		projectProdResult[id] = prod
	}

	// combine the data

	res := []tk.M{}

	for proj, v := range projects {
		wsavg := 0.0
		turbineList := v.(tk.M).Get("turbines").([]tk.M)

		for _, i := range turbineList {
			wsavg += i.GetFloat64("windspeed")
		}

		wsavg = tk.Div(wsavg, tk.ToFloat64(len(turbineList), 0, tk.RoundingAuto))
		v.(tk.M).Set("totalwsavg", wsavg)
		// v.(tk.M).Set("totalprod", v.(tk.M).GetFloat64("totalprod")/1000)
		v.(tk.M).Set("totalprod", projectProdResult[proj]/1000)

		v.(tk.M).Set("project", proj)
		res = append(res, v.(tk.M))
	}

	// get latest date update from ScadaDataHFD

	availDate := k.Session("availdate", "")
	date := availDate.(*Availdatedata).ScadaDataHFD[1].UTC()

	finalResult := tk.M{}
	finalResult.Set("data", res)
	finalResult.Set("timestamp", tk.M{"minute": date.Format("15:04"), "date": date.Format("02 Jan 2006")})

	data := struct {
		Data tk.M
	}{
		Data: finalResult,
	}

	return helper.CreateResult(true, data, "success")
}

func (m *MonitoringController) GetEvent(k *knot.WebContext) interface{} {
	k.Config.OutputType = knot.OutputJson

	p := tk.M{}
	e := k.GetPayload(&p)

	if e != nil {
		return helper.CreateResult(false, nil, e.Error())
	}

	turbine := p.Get("turbine").([]interface{})
	project := ""
	if p.GetString("project") != "" {
		anProject := strings.Split(p.GetString("project"), "(")
		project = strings.TrimRight(anProject[0], " ")
	}

	// log.Printf("%#v \n", project)

	match := tk.M{}
	var projectList []interface{}
	if project != "" {
		match.Set("project", project)
		projectList = append(projectList, project)
	}

	if len(turbine) > 0 {
		match.Set("turbine", tk.M{}.Set("$in", turbine))
	}

	var pipes []tk.M
	pipes = append(pipes, tk.M{}.Set("$match", match))
	pipes = append(pipes, tk.M{}.Set("$sort", tk.M{
		"timestamp": 1,
		"turbine":   1,
		"project":   1,
	}))

	csr, e := DB().Connection.NewQuery().
		From(new(MonitoringEvent).TableName()).
		Command("pipe", pipes).
		Cursor(nil)

	defer csr.Close()

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	results := make([]MonitoringEvent, 0)
	e = csr.Fetch(&results, 0, false)

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	data := struct {
		Data []MonitoringEvent
	}{
		Data: results,
	}

	return helper.CreateResult(true, data, "success")
}

func checkNAValue(val float64) (result float64) {
	if val == -9999999.0 {
		result = 0
	} else {
		result = val
	}

	return
}
<commit_msg>change ordering on getevent<commit_after>package controller

import (
	. "eaciit/wfdemo-git/library/core"
	. "eaciit/wfdemo-git/library/models"
	"strings"
	"time"

	"eaciit/wfdemo-git/web/helper"

	knot "github.com/eaciit/knot/knot.v1"
	tk "github.com/eaciit/toolkit"
)

type MonitoringController struct {
	App
}

func CreateMonitoringController() *MonitoringController {
	var controller = new(MonitoringController)
	return controller
}

func (m *MonitoringController) GetData(k *knot.WebContext) interface{} {
	k.Config.OutputType = knot.OutputJson

	p := tk.M{}
	e := k.GetPayload(&p)

	if e != nil {
		return helper.CreateResult(false, nil, e.Error())
	}

	// get the last data for monitoring

	turbine := p.Get("turbine").([]interface{})
	project := ""
	if p.GetString("project") != "" {
		anProject := strings.Split(p.GetString("project"), "(")
		project = strings.TrimRight(anProject[0], " ")
	}

	match := tk.M{}
	turbines := map[string]tk.M{}
	var projectList []interface{}
	if project != "" {
		match.Set("project", project)
		projectList = append(projectList, project)
	}

	turbines, _, _ = helper.GetProjectTurbineList(projectList)

	if len(turbine) > 0 {
		match.Set("turbine", tk.M{}.Set("$in", turbine))
	}

	group := tk.M{
		"_id": tk.M{
			"project": "$project",
			"turbine": "$turbine",
		},
		"timestamp":     tk.M{"$last": "$timestamp"},
		"windspeed":     tk.M{"$last": "$windspeed"},
		"production":    tk.M{"$last": "$production"},
		"rotorspeedrpm": tk.M{"$last": "$rotorspeedrpm"},
		"status":        tk.M{"$last": "$status"},
		"statuscode":    tk.M{"$last": "$statuscode"},
		"statusdesc":    tk.M{"$last": "$statusdesc"},
	}

	var pipes []tk.M
	pipes = append(pipes, tk.M{}.Set("$match", match))
	pipes = append(pipes, tk.M{}.Set("$group", group))
	pipes = append(pipes, tk.M{}.Set("$sort", tk.M{
		"_id.project": 1,
		"_id.turbine": 1,
	}))

	csr, e := DB().Connection.NewQuery().
		From(new(Monitoring).TableName()).
		Command("pipe", pipes).
		Cursor(nil)

	defer csr.Close()

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	results := make([]tk.M, 0)
	e = csr.Fetch(&results, 0, false)

	projects := tk.M{}

	for _, v := range results {
		ID := v.Get("_id").(tk.M)
		project := ID.GetString("project")
		turbine := ID.GetString("turbine")
		timestamp := v.Get("timestamp").(time.Time).UTC()
		windspeed := checkNAValue(v.GetFloat64("windspeed"))
		production := checkNAValue(v.GetFloat64("production"))
		rotorspeedrpm := checkNAValue(v.GetFloat64("rotorspeedrpm"))
		status := v.GetString("status")
		statuscode := v.GetString("statuscode")
		statusdesc := v.GetString("statusdesc")

		list := []tk.M{}
		newRecord := tk.M{}

		updated := tk.M{}
		capacitymw := 0.0

		if projects.Get(project) != nil {
			if projects.Get(project).(tk.M).Get("turbines") != nil {
				list = projects.Get(project).(tk.M).Get("turbines").([]tk.M)
			}
			updated = projects.Get(project).(tk.M)
		}

		if turbines[project].Get(turbine) != nil {
			capacitymw = turbines[project].Get(turbine).(TurbineMaster).CapacityMW
		}

		newRecord.Set("turbine", turbine)
		newRecord.Set("timestamp", timestamp)
		newRecord.Set("timestampstr", timestamp.Format("02-01-2006 15:04:05"))
		newRecord.Set("windspeed", windspeed)
		newRecord.Set("production", production)
		newRecord.Set("rotorspeedrpm", rotorspeedrpm)
		newRecord.Set("status", status)
		newRecord.Set("statuscode", statuscode)
		newRecord.Set("statusdesc", statusdesc)

		list = append(list, newRecord)
		updated.Set("turbines", list)
		updated.Set("totalturbines", updated.GetInt("totalturbines")+1)
		updated.Set("totalcap", updated.GetFloat64("totalcap")+capacitymw)
		updated.Set("totalprod", updated.GetFloat64("totalprod")+production)

		projects.Set(project, updated)
	}

	// get today total production

	match = tk.M{}
	if project != "" {
		match.Set("project", project)
	}

	if len(turbine) > 0 {
		match.Set("turbine", tk.M{}.Set("$in", turbine))
	}

	dateid, _ := time.Parse("20060102_0504", time.Now().Format("20060102_")+"_0000")
	match.Set("dateinfo.dateid", dateid.UTC())

	group = tk.M{
		"_id":        "$project",
		"production": tk.M{"$sum": "$production"},
	}

	pipes = []tk.M{}
	pipes = append(pipes, tk.M{}.Set("$match", match))
	pipes = append(pipes, tk.M{}.Set("$group", group))

	csr, e = DB().Connection.NewQuery().
		From(new(Monitoring).TableName()).
		Command("pipe", pipes).
		Cursor(nil)

	defer csr.Close()

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	results = make([]tk.M, 0)
	e = csr.Fetch(&results, 0, false)

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	projectProdResult := map[string]float64{}

	for _, v := range results {
		id := v.GetString("_id")
		prod := v.GetFloat64("production")
		projectProdResult[id] = prod
	}

	// combine the data

	res := []tk.M{}

	for proj, v := range projects {
		wsavg := 0.0
		turbineList := v.(tk.M).Get("turbines").([]tk.M)

		for _, i := range turbineList {
			wsavg += i.GetFloat64("windspeed")
		}

		wsavg = tk.Div(wsavg, tk.ToFloat64(len(turbineList), 0, tk.RoundingAuto))
		v.(tk.M).Set("totalwsavg", wsavg)
		// v.(tk.M).Set("totalprod", v.(tk.M).GetFloat64("totalprod")/1000)
		v.(tk.M).Set("totalprod", projectProdResult[proj]/1000)

		v.(tk.M).Set("project", proj)
		res = append(res, v.(tk.M))
	}

	// get latest date update from ScadaDataHFD

	availDate := k.Session("availdate", "")
	date := availDate.(*Availdatedata).ScadaDataHFD[1].UTC()

	finalResult := tk.M{}
	finalResult.Set("data", res)
	finalResult.Set("timestamp", tk.M{"minute": date.Format("15:04"), "date": date.Format("02 Jan 2006")})

	data := struct {
		Data tk.M
	}{
		Data: finalResult,
	}

	return helper.CreateResult(true, data, "success")
}

func (m *MonitoringController) GetEvent(k *knot.WebContext) interface{} {
	k.Config.OutputType = knot.OutputJson

	p := tk.M{}
	e := k.GetPayload(&p)

	if e != nil {
		return helper.CreateResult(false, nil, e.Error())
	}

	turbine := p.Get("turbine").([]interface{})
	project := ""
	if p.GetString("project") != "" {
		anProject := strings.Split(p.GetString("project"), "(")
		project = strings.TrimRight(anProject[0], " ")
	}

	// log.Printf("%#v \n", project)

	match := tk.M{}
	var projectList []interface{}
	if project != "" {
		match.Set("project", project)
		projectList = append(projectList, project)
	}

	if len(turbine) > 0 {
		match.Set("turbine", tk.M{}.Set("$in", turbine))
	}

	var pipes []tk.M
	pipes = append(pipes, tk.M{}.Set("$match", match))
	pipes = append(pipes, tk.M{}.Set("$sort", tk.M{
		"timestamp": -1,
		"turbine":   -1,
		"project":   -1,
	}))

	csr, e := DB().Connection.NewQuery().
		From(new(MonitoringEvent).TableName()).
		Command("pipe", pipes).
		Cursor(nil)

	defer csr.Close()

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	results := make([]MonitoringEvent, 0)
	e = csr.Fetch(&results, 0, false)

	if e != nil {
		return helper.CreateResult(false, nil, "Error query : "+e.Error())
	}

	data := struct {
		Data []MonitoringEvent
	}{
		Data: results,
	}

	return helper.CreateResult(true, data, "success")
}

func checkNAValue(val float64) (result float64) {
	if val == -9999999.0 {
		result = 0
	} else {
		result = val
	}

	return
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/rusenask/cron"

	"k8s.io/api/core/v1"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/keel-hq/keel/approvals"
	"github.com/keel-hq/keel/extension/notification"
	"github.com/keel-hq/keel/internal/k8s"
	"github.com/keel-hq/keel/internal/policy"
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/util/image"
	"github.com/keel-hq/keel/util/policies"

	log "github.com/sirupsen/logrus"
)

var kubernetesVersionedUpdatesCounter = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "kubernetes_versioned_updates_total",
		Help: "How many versioned deployments were updated, partitioned by deployment name.",
	},
	[]string{"kubernetes"},
)

var kubernetesUnversionedUpdatesCounter = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "kubernetes_unversioned_updates_total",
		Help: "How many unversioned deployments were updated, partitioned by deployment name.",
	},
	[]string{"kubernetes"},
)

func init() {
	prometheus.MustRegister(kubernetesVersionedUpdatesCounter)
	prometheus.MustRegister(kubernetesUnversionedUpdatesCounter)
}

// ProviderName - provider name
const ProviderName = "kubernetes"

var versionreg = regexp.MustCompile(`:[^:]*$`)

// GenericResourceCache an interface for generic resource cache.
type GenericResourceCache interface {
	// Values returns a copy of the contents of the cache.
	// The slice and its contents should be treated as read-only.
	Values() []*k8s.GenericResource

	// Register registers ch to receive a value when Notify is called.
	Register(chan int, int)
}

// UpdatePlan - deployment update plan
type UpdatePlan struct {
	// Updated deployment version
	// Deployment v1beta1.Deployment
	Resource *k8s.GenericResource

	// Current (last seen cluster version)
	CurrentVersion string
	// New version that's already in the deployment
	NewVersion string
}

func (p *UpdatePlan) String() string {
	if p.Resource != nil {
		return fmt.Sprintf("%s %s->%s", p.Resource.Identifier, p.CurrentVersion, p.NewVersion)
	}
	return "empty plan"
}

// Provider - kubernetes provider for auto update
type Provider struct {
	implementer Implementer

	sender notification.Sender

	approvalManager approvals.Manager

	cache GenericResourceCache

	events chan *types.Event
	stop   chan struct{}
}

// NewProvider - create new kubernetes based provider
func NewProvider(implementer Implementer, sender notification.Sender, approvalManager approvals.Manager, cache GenericResourceCache) (*Provider, error) {
	return &Provider{
		implementer:     implementer,
		cache:           cache,
		approvalManager: approvalManager,
		events:          make(chan *types.Event, 100),
		stop:            make(chan struct{}),
		sender:          sender,
	}, nil
}

// Submit - submit event to provider
func (p *Provider) Submit(event types.Event) error {
	p.events <- &event
	return nil
}

// GetName - get provider name
func (p *Provider) GetName() string {
	return ProviderName
}

// Start - starts kubernetes provider, waits for events
func (p *Provider) Start() error {
	return p.startInternal()
}

// Stop - stops kubernetes provider
func (p *Provider) Stop() {
	close(p.stop)
}

// TrackedImages returns a list of tracked images.
func (p *Provider) TrackedImages() ([]*types.TrackedImage, error) {
	var trackedImages []*types.TrackedImage

	for _, gr := range p.cache.Values() {
		labels := gr.GetLabels()

		// ignoring unlabelled deployments
		plc := policy.GetPolicyFromLabels(labels)
		if plc.Type() == policy.PolicyTypeNone {
			continue
		}

		annotations := gr.GetAnnotations()
		schedule, ok := annotations[types.KeelPollScheduleAnnotation]
		if ok {
			_, err := cron.Parse(schedule)
			if err != nil {
				log.WithFields(log.Fields{
					"error":     err,
					"schedule":  schedule,
					"name":      gr.Name,
					"namespace": gr.Namespace,
				}).Error("provider.kubernetes: failed to parse poll schedule, setting default schedule")
				schedule = types.KeelPollDefaultSchedule
			}
		} else {
			schedule = types.KeelPollDefaultSchedule
		}

		// trigger type, we only care for "poll" type triggers
		trigger := policies.GetTriggerPolicy(labels)
		secrets := gr.GetImagePullSecrets()
		images := gr.GetImages()
		for _, img := range images {
			ref, err := image.Parse(img)
			if err != nil {
				log.WithFields(log.Fields{
					"error":     err,
					"image":     img,
					"namespace": gr.Namespace,
					"name":      gr.Name,
				}).Error("provider.kubernetes: failed to parse image")
				continue
			}
			trackedImages = append(trackedImages, &types.TrackedImage{
				Image:        ref,
				PollSchedule: schedule,
				Trigger:      trigger,
				Provider:     ProviderName,
				Namespace:    gr.Namespace,
				Secrets:      secrets,
			})
		}
	}

	return trackedImages, nil
}

func (p *Provider) startInternal() error {
	for {
		select {
		case event := <-p.events:
			log.WithFields(log.Fields{
				"repository": event.Repository.Name,
				"tag":        event.Repository.Tag,
				"registry":   event.Repository.Host,
			}).Info("provider.kubernetes: processing event")
			_, err := p.processEvent(event)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"image": event.Repository.Name,
					"tag":   event.Repository.Tag,
				}).Error("provider.kubernetes: failed to process event")
			}
		case <-p.stop:
			log.Info("provider.kubernetes: got shutdown signal, stopping...")
			return nil
		}
	}
}

func (p *Provider) processEvent(event *types.Event) (updated []*k8s.GenericResource, err error) {
	plans, err := p.createUpdatePlans(&event.Repository)
	if err != nil {
		return nil, err
	}

	if len(plans) == 0 {
		log.WithFields(log.Fields{
			"image": event.Repository.Name,
			"tag":   event.Repository.Tag,
		}).Info("provider.kubernetes: no plans for deployment updates found for this event")
		return
	}

	approvedPlans := p.checkForApprovals(event, plans)

	return p.updateDeployments(approvedPlans)
}

func (p *Provider) updateDeployments(plans []*UpdatePlan) (updated []*k8s.GenericResource, err error) {
	for _, plan := range plans {
		resource := plan.Resource

		annotations := resource.GetAnnotations()

		notificationChannels := types.ParseEventNotificationChannels(annotations)

		p.sender.Send(types.EventNotification{
			Name:      "preparing to update resource",
			Message:   fmt.Sprintf("Preparing to update %s %s/%s %s->%s (%s)", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", ")),
			CreatedAt: time.Now(),
			Type:      types.NotificationPreDeploymentUpdate,
			Level:     types.LevelDebug,
			Channels:  notificationChannels,
		})

		var err error

		annotations["kubernetes.io/change-cause"] = fmt.Sprintf("keel automated update, version %s -> %s", plan.CurrentVersion, plan.NewVersion)

		resource.SetAnnotations(annotations)

		err = p.implementer.Update(resource)
		kubernetesVersionedUpdatesCounter.With(prometheus.Labels{"kubernetes": fmt.Sprintf("%s/%s", resource.Namespace, resource.Name)}).Inc()
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"namespace":  resource.Namespace,
				"deployment": resource.Name,
				"kind":       resource.Kind(),
				"update":     fmt.Sprintf("%s->%s", plan.CurrentVersion, plan.NewVersion),
			}).Error("provider.kubernetes: got error while updating resource")

			p.sender.Send(types.EventNotification{
				Name:      "update resource",
				Message:   fmt.Sprintf("%s %s/%s update %s->%s failed, error: %s", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, err),
				CreatedAt: time.Now(),
				Type:      types.NotificationDeploymentUpdate,
				Level:     types.LevelError,
				Channels:  notificationChannels,
			})

			continue
		}

		err = p.updateComplete(plan)
		if err != nil {
			log.WithFields(log.Fields{
				"error":     err,
				"name":      resource.Name,
				"kind":      resource.Kind(),
				"namespace": resource.Namespace,
			}).Warn("provider.kubernetes: got error while resetting approvals counter after successful update")
		}

		var msg string
		releaseNotes := types.ParseReleaseNotesURL(resource.GetAnnotations())
		if releaseNotes != "" {
			msg = fmt.Sprintf("Successfully updated %s %s/%s %s->%s (%s). Release notes: %s", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", "), releaseNotes)
		} else {
			msg = fmt.Sprintf("Successfully updated %s %s/%s %s->%s (%s)", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", "))
		}

		p.sender.Send(types.EventNotification{
			Name:      "update resource",
			Message:   msg,
			CreatedAt: time.Now(),
			Type:      types.NotificationDeploymentUpdate,
			Level:     types.LevelSuccess,
			Channels:  notificationChannels,
		})

		log.WithFields(log.Fields{
			"name":      resource.Name,
			"kind":      resource.Kind(),
			"previous":  plan.CurrentVersion,
			"new":       plan.NewVersion,
			"namespace": resource.Namespace,
		}).Info("provider.kubernetes: resource updated")
		updated = append(updated, resource)
	}

	return
}

func getDesiredImage(delta map[string]string, currentImage string) (string, error) {
	currentRef, err := image.Parse(currentImage)
	if err != nil {
		return "", err
	}
	for repository, tag := range delta {
		if repository == currentRef.Repository() {
			ref, err := image.Parse(repository)
			if err != nil {
				return "", err
			}

			// updating image
			if ref.Registry() == image.DefaultRegistryHostname {
				return fmt.Sprintf("%s:%s", ref.ShortName(), tag), nil
			}
			return fmt.Sprintf("%s:%s", ref.Repository(), tag), nil
		}
	}
	return "", fmt.Errorf("image %s not found in deltas", currentImage)
}

// createUpdatePlans - impacted deployments by changed repository
func (p *Provider) createUpdatePlans(repo *types.Repository) ([]*UpdatePlan, error) {
	impacted := []*UpdatePlan{}

	for _, resource := range p.cache.Values() {

		labels := resource.GetLabels()

		plc := policy.GetPolicyFromLabels(labels)
		if plc.Type() == policy.PolicyTypeNone {
			log.Debugf("no policy defined, skipping: %s, labels: %s", resource.Identifier, labels)
			continue
		}
		log.Infof("keel policy for %s found: %s", resource.Identifier, plc.Name())

		updated, shouldUpdateDeployment, err := checkForUpdate(plc, repo, resource)
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"deployment": resource.Name,
				"kind":       resource.Kind(),
				"namespace":  resource.Namespace,
			}).Error("provider.kubernetes: got error while checking versioned resource")
			continue
		}

		if shouldUpdateDeployment {
			impacted = append(impacted, updated)
		}
	}

	return impacted, nil
}

func (p *Provider) namespaces() (*v1.NamespaceList, error) {
	return p.implementer.Namespaces()
}
<commit_msg>redundant logging<commit_after>package kubernetes

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/rusenask/cron"

	"k8s.io/api/core/v1"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/keel-hq/keel/approvals"
	"github.com/keel-hq/keel/extension/notification"
	"github.com/keel-hq/keel/internal/k8s"
	"github.com/keel-hq/keel/internal/policy"
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/util/image"
	"github.com/keel-hq/keel/util/policies"

	log "github.com/sirupsen/logrus"
)

var kubernetesVersionedUpdatesCounter = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "kubernetes_versioned_updates_total",
		Help: "How many versioned deployments were updated, partitioned by deployment name.",
	},
	[]string{"kubernetes"},
)

var kubernetesUnversionedUpdatesCounter = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "kubernetes_unversioned_updates_total",
		Help: "How many unversioned deployments were updated, partitioned by deployment name.",
	},
	[]string{"kubernetes"},
)

func init() {
	prometheus.MustRegister(kubernetesVersionedUpdatesCounter)
	prometheus.MustRegister(kubernetesUnversionedUpdatesCounter)
}

// ProviderName - provider name
const ProviderName = "kubernetes"

var versionreg = regexp.MustCompile(`:[^:]*$`)

// GenericResourceCache an interface for generic resource cache.
type GenericResourceCache interface {
	// Values returns a copy of the contents of the cache.
	// The slice and its contents should be treated as read-only.
	Values() []*k8s.GenericResource

	// Register registers ch to receive a value when Notify is called.
	Register(chan int, int)
}

// UpdatePlan - deployment update plan
type UpdatePlan struct {
	// Updated deployment version
	// Deployment v1beta1.Deployment
	Resource *k8s.GenericResource

	// Current (last seen cluster version)
	CurrentVersion string
	// New version that's already in the deployment
	NewVersion string
}

func (p *UpdatePlan) String() string {
	if p.Resource != nil {
		return fmt.Sprintf("%s %s->%s", p.Resource.Identifier, p.CurrentVersion, p.NewVersion)
	}
	return "empty plan"
}

// Provider - kubernetes provider for auto update
type Provider struct {
	implementer Implementer

	sender notification.Sender

	approvalManager approvals.Manager

	cache GenericResourceCache

	events chan *types.Event
	stop   chan struct{}
}

// NewProvider - create new kubernetes based provider
func NewProvider(implementer Implementer, sender notification.Sender, approvalManager approvals.Manager, cache GenericResourceCache) (*Provider, error) {
	return &Provider{
		implementer:     implementer,
		cache:           cache,
		approvalManager: approvalManager,
		events:          make(chan *types.Event, 100),
		stop:            make(chan struct{}),
		sender:          sender,
	}, nil
}

// Submit - submit event to provider
func (p *Provider) Submit(event types.Event) error {
	p.events <- &event
	return nil
}

// GetName - get provider name
func (p *Provider) GetName() string {
	return ProviderName
}

// Start - starts kubernetes provider, waits for events
func (p *Provider) Start() error {
	return p.startInternal()
}

// Stop - stops kubernetes provider
func (p *Provider) Stop() {
	close(p.stop)
}

// TrackedImages returns a list of tracked images.
func (p *Provider) TrackedImages() ([]*types.TrackedImage, error) {
	var trackedImages []*types.TrackedImage

	for _, gr := range p.cache.Values() {
		labels := gr.GetLabels()

		// ignoring unlabelled deployments
		plc := policy.GetPolicyFromLabels(labels)
		if plc.Type() == policy.PolicyTypeNone {
			continue
		}

		annotations := gr.GetAnnotations()
		schedule, ok := annotations[types.KeelPollScheduleAnnotation]
		if ok {
			_, err := cron.Parse(schedule)
			if err != nil {
				log.WithFields(log.Fields{
					"error":     err,
					"schedule":  schedule,
					"name":      gr.Name,
					"namespace": gr.Namespace,
				}).Error("provider.kubernetes: failed to parse poll schedule, setting default schedule")
				schedule = types.KeelPollDefaultSchedule
			}
		} else {
			schedule = types.KeelPollDefaultSchedule
		}

		// trigger type, we only care for "poll" type triggers
		trigger := policies.GetTriggerPolicy(labels)
		secrets := gr.GetImagePullSecrets()
		images := gr.GetImages()
		for _, img := range images {
			ref, err := image.Parse(img)
			if err != nil {
				log.WithFields(log.Fields{
					"error":     err,
					"image":     img,
					"namespace": gr.Namespace,
					"name":      gr.Name,
				}).Error("provider.kubernetes: failed to parse image")
				continue
			}
			trackedImages = append(trackedImages, &types.TrackedImage{
				Image:        ref,
				PollSchedule: schedule,
				Trigger:      trigger,
				Provider:     ProviderName,
				Namespace:    gr.Namespace,
				Secrets:      secrets,
			})
		}
	}

	return trackedImages, nil
}

func (p *Provider) startInternal() error {
	for {
		select {
		case event := <-p.events:
			log.WithFields(log.Fields{
				"repository": event.Repository.Name,
				"tag":        event.Repository.Tag,
				"registry":   event.Repository.Host,
			}).Info("provider.kubernetes: processing event")
			_, err := p.processEvent(event)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"image": event.Repository.Name,
					"tag":   event.Repository.Tag,
				}).Error("provider.kubernetes: failed to process event")
			}
		case <-p.stop:
			log.Info("provider.kubernetes: got shutdown signal, stopping...")
			return nil
		}
	}
}

func (p *Provider) processEvent(event *types.Event) (updated []*k8s.GenericResource, err error) {
	plans, err := p.createUpdatePlans(&event.Repository)
	if err != nil {
		return nil, err
	}

	if len(plans) == 0 {
		log.WithFields(log.Fields{
			"image": event.Repository.Name,
			"tag":   event.Repository.Tag,
		}).Info("provider.kubernetes: no plans for deployment updates found for this event")
		return
	}

	approvedPlans := p.checkForApprovals(event, plans)

	return p.updateDeployments(approvedPlans)
}

func (p *Provider) updateDeployments(plans []*UpdatePlan) (updated []*k8s.GenericResource, err error) {
	for _, plan := range plans {
		resource := plan.Resource

		annotations := resource.GetAnnotations()

		notificationChannels := types.ParseEventNotificationChannels(annotations)

		p.sender.Send(types.EventNotification{
			Name:      "preparing to update resource",
			Message:   fmt.Sprintf("Preparing to update %s %s/%s %s->%s (%s)", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", ")),
			CreatedAt: time.Now(),
			Type:      types.NotificationPreDeploymentUpdate,
			Level:     types.LevelDebug,
			Channels:  notificationChannels,
		})

		var err error

		annotations["kubernetes.io/change-cause"] = fmt.Sprintf("keel automated update, version %s -> %s", plan.CurrentVersion, plan.NewVersion)

		resource.SetAnnotations(annotations)

		err = p.implementer.Update(resource)
		kubernetesVersionedUpdatesCounter.With(prometheus.Labels{"kubernetes": fmt.Sprintf("%s/%s", resource.Namespace, resource.Name)}).Inc()
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"namespace":  resource.Namespace,
				"deployment": resource.Name,
				"kind":       resource.Kind(),
				"update":     fmt.Sprintf("%s->%s", plan.CurrentVersion, plan.NewVersion),
			}).Error("provider.kubernetes: got error while updating resource")

			p.sender.Send(types.EventNotification{
				Name:      "update resource",
				Message:   fmt.Sprintf("%s %s/%s update %s->%s failed, error: %s", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, err),
				CreatedAt: time.Now(),
				Type:      types.NotificationDeploymentUpdate,
				Level:     types.LevelError,
				Channels:  notificationChannels,
			})

			continue
		}

		err = p.updateComplete(plan)
		if err != nil {
			log.WithFields(log.Fields{
				"error":     err,
				"name":      resource.Name,
				"kind":      resource.Kind(),
				"namespace": resource.Namespace,
			}).Warn("provider.kubernetes: got error while resetting approvals counter after successful update")
		}

		var msg string
		releaseNotes := types.ParseReleaseNotesURL(resource.GetAnnotations())
		if releaseNotes != "" {
			msg = fmt.Sprintf("Successfully updated %s %s/%s %s->%s (%s). Release notes: %s", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", "), releaseNotes)
		} else {
			msg = fmt.Sprintf("Successfully updated %s %s/%s %s->%s (%s)", resource.Kind(), resource.Namespace, resource.Name, plan.CurrentVersion, plan.NewVersion, strings.Join(resource.GetImages(), ", "))
		}

		p.sender.Send(types.EventNotification{
			Name:      "update resource",
			Message:   msg,
			CreatedAt: time.Now(),
			Type:      types.NotificationDeploymentUpdate,
			Level:     types.LevelSuccess,
			Channels:  notificationChannels,
		})

		log.WithFields(log.Fields{
			"name":      resource.Name,
			"kind":      resource.Kind(),
			"previous":  plan.CurrentVersion,
			"new":       plan.NewVersion,
			"namespace": resource.Namespace,
		}).Info("provider.kubernetes: resource updated")
		updated = append(updated, resource)
	}

	return
}

func getDesiredImage(delta map[string]string, currentImage string) (string, error) {
	currentRef, err := image.Parse(currentImage)
	if err != nil {
		return "", err
	}
	for repository, tag := range delta {
		if repository == currentRef.Repository() {
			ref, err := image.Parse(repository)
			if err != nil {
				return "", err
			}

			// updating image
			if ref.Registry() == image.DefaultRegistryHostname {
				return fmt.Sprintf("%s:%s", ref.ShortName(), tag), nil
			}
			return fmt.Sprintf("%s:%s", ref.Repository(), tag), nil
		}
	}
	return "", fmt.Errorf("image %s not found in deltas", currentImage)
}

// createUpdatePlans - impacted deployments by changed repository
func (p *Provider) createUpdatePlans(repo *types.Repository) ([]*UpdatePlan, error) {
	impacted := []*UpdatePlan{}

	for _, resource := range p.cache.Values() {

		labels := resource.GetLabels()

		plc := policy.GetPolicyFromLabels(labels)
		if plc.Type() == policy.PolicyTypeNone {
			log.Debugf("no policy defined, skipping: %s, labels: %s", resource.Identifier, labels)
			continue
		}

		updated, shouldUpdateDeployment, err := checkForUpdate(plc, repo, resource)
		if err != nil {
			log.WithFields(log.Fields{
				"error":      err,
				"deployment": resource.Name,
				"kind":       resource.Kind(),
				"namespace":  resource.Namespace,
			}).Error("provider.kubernetes: got error while checking versioned resource")
			continue
		}

		if shouldUpdateDeployment {
			impacted = append(impacted, updated)
		}
	}

	return impacted, nil
}

func (p *Provider) namespaces() (*v1.NamespaceList, error) {
	return p.implementer.Namespaces()
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 graph_test

import (
	"fmt"
	"math/rand"
	"testing"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/graph"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestReverseTopsort(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type ReverseTopsortTreeTest struct {
	ctx context.Context
}

var _ SetUpInterface = &ReverseTopsortTreeTest{}

func init() { RegisterTestSuite(&ReverseTopsortTreeTest{}) }

func (t *ReverseTopsortTreeTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
}

func (t *ReverseTopsortTreeTest) run(
	root string,
	edges map[string][]string) (nodes []string, err error) {
	// Set up a successor finder.
	sf := &successorFinder{
		F: func(ctx context.Context, n string) (successors []string, err error) {
			successors = edges[n]
			return
		},
	}

	// Call through.
	c := make(chan graph.Node, 10e3)
	err = graph.ReverseTopsortTree(
		t.ctx,
		sf,
		root,
		c)

	if err != nil {
		err = fmt.Errorf("ReverseTopsortTree: %v", err)
		return
	}

	// Convert the nodes returned in the channel.
	close(c)
	for n := range c {
		nodes = append(nodes, n.(string))
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ReverseTopsortTreeTest) SingleNode() {
	// Graph structure:
	//
	//        A
	//
	root := "A"
	edges := map[string][]string{}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	ExpectThat(nodes, ElementsAre("A"))
}

func (t *ReverseTopsortTreeTest) NoBranching() {
	// Graph structure:
	//
	//        A
	//        |
	//        B
	//        |
	//        C
	//
	root := "A"
	edges := map[string][]string{
		"A": {"B"},
		"B": {"C"},
	}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	AssertThat(
		sortNodes(nodes),
		ElementsAre(
			"A",
			"B",
			"C",
		))

	nodeIndex := indexNodes(nodes)
	for p, successors := range edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[s], nodeIndex[p], "%q -> %q", p, s)
		}
	}
}

func (t *ReverseTopsortTreeTest) LittleBranching() {
	// Graph structure:
	//
	//        A
	//      / |
	//     B  D
	//     |  |
	//     C  E
	//
	root := "A"
	edges := map[string][]string{
		"A": {"B", "D"},
		"B": {"C"},
		"D": {"E"},
	}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	AssertThat(
		sortNodes(nodes),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
		))

	nodeIndex := indexNodes(nodes)
	for p, successors := range edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[s], nodeIndex[p], "%q -> %q", p, s)
		}
	}
}

func (t *ReverseTopsortTreeTest) LotsOfBranching() {
	// Graph structure:
	//
	//        A
	//      / |
	//     B  C
	//      / | \
	//     D  E  F
	//      / |  |
	//     G  H  I
	//           | \
	//           J  K
	//
	root := "A"
	edges := map[string][]string{
		"A": []string{"B", "C"},
		"C": []string{"D", "E", "F"},
		"E": []string{"G", "H"},
		"F": []string{"I"},
		"I": []string{"J", "K"},
	}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	AssertThat(
		sortNodes(nodes),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
			"F",
			"G",
			"H",
			"I",
			"J",
			"K",
		))

	nodeIndex := indexNodes(nodes)
	for p, successors := range edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[s], nodeIndex[p], "%q -> %q", p, s)
		}
	}
}

func (t *ReverseTopsortTreeTest) LargeTree() {
	// Set up a tree of the given depth, with a random number of children for
	// each node.
	const depth = 10
	root := "root"

	nextID := 0
	nextLevel := []string{"root"}
	allNodes := map[string]struct{}{
		"root": struct{}{},
	}

	edges := map[string][]string{}
	for depthI := 0; depthI < depth; depthI++ {
		thisLevel := nextLevel
		nextLevel = nil
		for _, parent := range thisLevel {
			numChildren := int(rand.Int31n(6))
			for childI := 0; childI < numChildren; childI++ {
				child := fmt.Sprintf("%v", nextID)
				nextID++

				nextLevel = append(nextLevel, child)
				edges[parent] = append(edges[parent], child)
				allNodes[child] = struct{}{}
			}
		}
	}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	// All nodes should be represented.
	AssertEq(len(allNodes), len(nodes))
	for _, n := range nodes {
		_, ok := allNodes[n]
		AssertTrue(ok, "Unexpected node: %q", n)
	}

	// Edge order should be respected.
	nodeIndex := indexNodes(nodes)
	for p, successors := range edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[s], nodeIndex[p], "%q -> %q", p, s)
		}
	}
}
<commit_msg>Share with ReverseTopsortTreeTest.LargeTree.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 graph_test

import (
	"fmt"
	"testing"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/graph"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestReverseTopsort(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type ReverseTopsortTreeTest struct {
	ctx context.Context
}

var _ SetUpInterface = &ReverseTopsortTreeTest{}

func init() { RegisterTestSuite(&ReverseTopsortTreeTest{}) }

func (t *ReverseTopsortTreeTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
}

func (t *ReverseTopsortTreeTest) run(
	root string,
	edges map[string][]string) (nodes []string, err error) {
	// Set up a successor finder.
	sf := &successorFinder{
		F: func(ctx context.Context, n string) (successors []string, err error) {
			successors = edges[n]
			return
		},
	}

	// Call through.
	c := make(chan graph.Node, 10e3)
	err = graph.ReverseTopsortTree(
		t.ctx,
		sf,
		root,
		c)

	if err != nil {
		err = fmt.Errorf("ReverseTopsortTree: %v", err)
		return
	}

	// Convert the nodes returned in the channel.
	close(c)
	for n := range c {
		nodes = append(nodes, n.(string))
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ReverseTopsortTreeTest) SingleNode() {
	// Graph structure:
	//
	//        A
	//
	root := "A"
	edges := map[string][]string{}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	ExpectThat(nodes, ElementsAre("A"))
}

func (t *ReverseTopsortTreeTest) NoBranching() {
	// Graph structure:
	//
	//        A
	//        |
	//        B
	//        |
	//        C
	//
	root := "A"
	edges := map[string][]string{
		"A": {"B"},
		"B": {"C"},
	}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	AssertThat(
		sortNodes(nodes),
		ElementsAre(
			"A",
			"B",
			"C",
		))

	nodeIndex := indexNodes(nodes)
	for p, successors := range edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[s], nodeIndex[p], "%q -> %q", p, s)
		}
	}
}

func (t *ReverseTopsortTreeTest) LittleBranching() {
	// Graph structure:
	//
	//        A
	//      / |
	//     B  D
	//     |  |
	//     C  E
	//
	root := "A"
	edges := map[string][]string{
		"A": {"B", "D"},
		"B": {"C"},
		"D": {"E"},
	}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	AssertThat(
		sortNodes(nodes),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
		))

	nodeIndex := indexNodes(nodes)
	for p, successors := range edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[s], nodeIndex[p], "%q -> %q", p, s)
		}
	}
}

func (t *ReverseTopsortTreeTest) LotsOfBranching() {
	// Graph structure:
	//
	//        A
	//      / |
	//     B  C
	//      / | \
	//     D  E  F
	//      / |  |
	//     G  H  I
	//           | \
	//           J  K
	//
	root := "A"
	edges := map[string][]string{
		"A": []string{"B", "C"},
		"C": []string{"D", "E", "F"},
		"E": []string{"G", "H"},
		"F": []string{"I"},
		"I": []string{"J", "K"},
	}

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	AssertThat(
		sortNodes(nodes),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
			"F",
			"G",
			"H",
			"I",
			"J",
			"K",
		))

	nodeIndex := indexNodes(nodes)
	for p, successors := range edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[s], nodeIndex[p], "%q -> %q", p, s)
		}
	}
}

func (t *ReverseTopsortTreeTest) LargeTree() {
	const depth = 10
	edges := randomTree(depth)
	root := "root"

	// Call
	nodes, err := t.run(root, edges)
	AssertEq(nil, err)

	// All nodes should be represented.
	AssertEq(len(edges), len(nodes))
	for _, n := range nodes {
		_, ok := edges[n]
		AssertTrue(ok, "Unexpected node: %q", n)
	}

	// Edge order should be respected.
	nodeIndex := indexNodes(nodes)
	for p, successors := range edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[s], nodeIndex[p], "%q -> %q", p, s)
		}
	}
}
<|endoftext|>


<commit_before>package terraform_clc

import (
	"encoding/json"
	"fmt"

	clc "github.com/CenturyLinkCloud/clc-sdk"
	"github.com/CenturyLinkCloud/clc-sdk/lb"

	"github.com/hashicorp/terraform/helper/schema"
)

func resourceCLCLoadBalancer() *schema.Resource {
	return &schema.Resource{
		Create: resourceCLCLoadBalancerCreate,
		Read:   resourceCLCLoadBalancerRead,
		Update: resourceCLCLoadBalancerUpdate,
		Delete: resourceCLCLoadBalancerDelete,
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"data_center": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"status": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			// optional
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			// computed
			"ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"pools": &schema.Schema{
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeMap},
			},
		},
	}
}

func resourceCLCLoadBalancerCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*clc.Client)
	dc := d.Get("data_center").(string)
	name := d.Get("name").(string)
	desc := d.Get("description").(string)
	status := d.Get("status").(string)
	r1 := lb.LoadBalancer{
		Name:        name,
		Description: desc,
		Status:      status,
	}
	l, err := client.LB.Create(dc, r1)
	if err != nil {
		return fmt.Errorf("Failed creating load balancer under %v/%v: %v", dc, name, err)
	}
	d.SetId(l.ID)

	a1, _ := json.Marshal(l)
	LOG.Println(string(a1))
	return resourceCLCLoadBalancerRead(d, meta)
}

func resourceCLCLoadBalancerRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*clc.Client)
	dc := d.Get("data_center").(string)
	lbname := d.Id()
	resp, err := client.LB.Get(dc, lbname)
	if err != nil {
		LOG.Printf("Failed finding load balancer %v/%v. Marking destroyed", dc, lbname)
		d.SetId("")
		return nil
	}
	d.Set("description", resp.Description)
	d.Set("ip_address", resp.IPaddress)
	d.Set("status", resp.Status)
	d.Set("pools", resp.Pools)
	d.Set("links", resp.Links)
	return nil
}

func resourceCLCLoadBalancerUpdate(d *schema.ResourceData, meta interface{}) error {
	//client := meta.(*clc.Client)
	update := lb.LoadBalancer{}
	client := meta.(*clc.Client)
	dc := d.Get("data_center").(string)
	id := d.Id()

	if d.HasChange("name") {
		d.SetPartial("name")
		update.Status = d.Get("name").(string)
	}
	if d.HasChange("description") {
		d.SetPartial("description")
		update.Status = d.Get("description").(string)
	}
	if d.HasChange("status") {
		d.SetPartial("status")
		update.Status = d.Get("status").(string)
	}
	if update.Name != "" || update.Description != "" || update.Status != "" {
		client.LB.Update(dc, id, update)
	}
	return nil
}

func resourceCLCLoadBalancerDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*clc.Client)
	dc := d.Get("data_center").(string)
	id := d.Id()
	err := client.LB.Delete(dc, id)
	if err != nil {
		return fmt.Errorf("Failed deleting loadbalancer %v: %v", id, err)
	}
	return nil
}
<commit_msg>remove sleep hack. just wait til next read<commit_after>package terraform_clc

import (
	"encoding/json"
	"fmt"

	clc "github.com/CenturyLinkCloud/clc-sdk"
	"github.com/CenturyLinkCloud/clc-sdk/lb"

	"github.com/hashicorp/terraform/helper/schema"
)

func resourceCLCLoadBalancer() *schema.Resource {
	return &schema.Resource{
		Create: resourceCLCLoadBalancerCreate,
		Read:   resourceCLCLoadBalancerRead,
		Update: resourceCLCLoadBalancerUpdate,
		Delete: resourceCLCLoadBalancerDelete,
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"data_center": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"status": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			// optional
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			// computed
			"ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"pools": &schema.Schema{
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeMap},
			},
		},
	}
}

func resourceCLCLoadBalancerCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*clc.Client)
	dc := d.Get("data_center").(string)
	name := d.Get("name").(string)
	desc := d.Get("description").(string)
	status := d.Get("status").(string)
	r1 := lb.LoadBalancer{
		Name:        name,
		Description: desc,
		Status:      status,
	}
	l, err := client.LB.Create(dc, r1)
	if err != nil {
		return fmt.Errorf("Failed creating load balancer under %v/%v: %v", dc, name, err)
	}
	d.SetId(l.ID)
	a1, _ := json.Marshal(l)
	LOG.Println(string(a1))
	// it's created, but possible race condition if we query here
	return nil
}

func resourceCLCLoadBalancerRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*clc.Client)
	dc := d.Get("data_center").(string)
	id := d.Id()
	resp, err := client.LB.Get(dc, id)
	if err != nil {
		LOG.Printf("Failed finding load balancer %v/%v. Marking destroyed", dc, id)
		d.SetId("")
		return nil
	}
	d.Set("description", resp.Description)
	d.Set("ip_address", resp.IPaddress)
	d.Set("status", resp.Status)
	d.Set("pools", resp.Pools)
	d.Set("links", resp.Links)
	return nil
}

func resourceCLCLoadBalancerUpdate(d *schema.ResourceData, meta interface{}) error {
	//client := meta.(*clc.Client)
	update := lb.LoadBalancer{}
	client := meta.(*clc.Client)
	dc := d.Get("data_center").(string)
	id := d.Id()

	if d.HasChange("name") {
		d.SetPartial("name")
		update.Status = d.Get("name").(string)
	}
	if d.HasChange("description") {
		d.SetPartial("description")
		update.Status = d.Get("description").(string)
	}
	if d.HasChange("status") {
		d.SetPartial("status")
		update.Status = d.Get("status").(string)
	}
	if update.Name != "" || update.Description != "" || update.Status != "" {
		client.LB.Update(dc, id, update)
	}
	return nil
}

func resourceCLCLoadBalancerDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*clc.Client)
	dc := d.Get("data_center").(string)
	id := d.Id()
	err := client.LB.Delete(dc, id)
	if err != nil {
		return fmt.Errorf("Failed deleting loadbalancer %v: %v", id, err)
	}
	return nil
}
<|endoftext|>


<commit_before>package devops

import (
	. "github.com/influxdata/influxdb-comparisons/bulk_data_gen/common"
	"time"
)

// A DevopsSimulator generates data similar to telemetry from Telegraf.
// It fulfills the Simulator interface.
type DevopsSimulator struct {
	madePoints int64
	madeValues int64
	maxPoints  int64

	simulatedMeasurementIndex int

	hostIndex int
	hosts     []Host

	timestampNow   time.Time
	timestampStart time.Time
	timestampEnd   time.Time
}

func (g *DevopsSimulator) SeenPoints() int64 {
	return g.madePoints
}

func (g *DevopsSimulator) SeenValues() int64 {
	return g.madeValues
}

func (g *DevopsSimulator) Total() int64 {
	return g.maxPoints
}

func (g *DevopsSimulator) Finished() bool {
	return g.madePoints >= g.maxPoints
}

// Type DevopsSimulatorConfig is used to create a DevopsSimulator.
type DevopsSimulatorConfig struct {
	Start time.Time
	End   time.Time

	HostCount int64
	HostOffset int64
}

func (d *DevopsSimulatorConfig) ToSimulator() *DevopsSimulator {
	hostInfos := make([]Host, d.HostCount)
	for i := 0; i < len(hostInfos); i++ {
		hostInfos[i] = NewHost(i, int(d.HostOffset), d.Start)
	}

	epochs := d.End.Sub(d.Start).Nanoseconds() / EpochDuration.Nanoseconds()
	maxPoints := epochs * (d.HostCount * NHostSims)
	dg := &DevopsSimulator{
		madePoints: 0,
		madeValues: 0,
		maxPoints:  maxPoints,

		simulatedMeasurementIndex: 0,

		hostIndex: 0,
		hosts:     hostInfos,

		timestampNow:   d.Start,
		timestampStart: d.Start,
		timestampEnd:   d.End,
	}

	return dg
}

// Next advances a Point to the next state in the generator.
func (d *DevopsSimulator) Next(p *Point) {
	// switch to the next metric if needed
	if d.hostIndex == len(d.hosts) {
		d.hostIndex = 0
		d.simulatedMeasurementIndex++
	}

	if d.simulatedMeasurementIndex == NHostSims {
		d.simulatedMeasurementIndex = 0

		for i := 0; i < len(d.hosts); i++ {
			d.hosts[i].TickAll(EpochDuration)
		}
	}

	host := &d.hosts[d.hostIndex]

	// Populate host-specific tags:
	p.AppendTag(MachineTagKeys[0], host.Name)
	p.AppendTag(MachineTagKeys[1], host.Region)
	p.AppendTag(MachineTagKeys[2], host.Datacenter)
	p.AppendTag(MachineTagKeys[3], host.Rack)
	p.AppendTag(MachineTagKeys[4], host.OS)
	p.AppendTag(MachineTagKeys[5], host.Arch)
	p.AppendTag(MachineTagKeys[6], host.Team)
	p.AppendTag(MachineTagKeys[7], host.Service)
	p.AppendTag(MachineTagKeys[8], host.ServiceVersion)
	p.AppendTag(MachineTagKeys[9], host.ServiceEnvironment)

	// Populate measurement-specific tags and fields:
	host.SimulatedMeasurements[d.simulatedMeasurementIndex].ToPoint(p)

	d.madePoints++
	d.hostIndex++
	d.madeValues += int64(len(p.FieldValues))

	return
}
<commit_msg>exact values counting<commit_after>package devops

import (
	. "github.com/influxdata/influxdb-comparisons/bulk_data_gen/common"
	"time"
)

// A DevopsSimulator generates data similar to telemetry from Telegraf.
// It fulfills the Simulator interface.
type DevopsSimulator struct {
	madePoints int64
	madeValues int64
	maxPoints  int64

	simulatedMeasurementIndex int

	hostIndex int
	hosts     []Host

	timestampNow   time.Time
	timestampStart time.Time
	timestampEnd   time.Time
}

func (g *DevopsSimulator) SeenPoints() int64 {
	return g.madePoints
}

func (g *DevopsSimulator) SeenValues() int64 {
	return g.madeValues
}

func (g *DevopsSimulator) Total() int64 {
	return g.maxPoints
}

func (g *DevopsSimulator) Finished() bool {
	return g.madePoints >= g.maxPoints
}

// Type DevopsSimulatorConfig is used to create a DevopsSimulator.
type DevopsSimulatorConfig struct {
	Start time.Time
	End   time.Time

	HostCount int64
	HostOffset int64
}

func (d *DevopsSimulatorConfig) ToSimulator() *DevopsSimulator {
	hostInfos := make([]Host, d.HostCount)
	for i := 0; i < len(hostInfos); i++ {
		hostInfos[i] = NewHost(i, int(d.HostOffset), d.Start)
	}

	epochs := d.End.Sub(d.Start).Nanoseconds() / EpochDuration.Nanoseconds()
	maxPoints := epochs * (d.HostCount * NHostSims)
	dg := &DevopsSimulator{
		madePoints: 0,
		madeValues: 0,
		maxPoints:  maxPoints,

		simulatedMeasurementIndex: 0,

		hostIndex: 0,
		hosts:     hostInfos,

		timestampNow:   d.Start,
		timestampStart: d.Start,
		timestampEnd:   d.End,
	}

	return dg
}

// Next advances a Point to the next state in the generator.
func (d *DevopsSimulator) Next(p *Point) {
	// switch to the next host if needed
	if d.simulatedMeasurementIndex == NHostSims {
		d.simulatedMeasurementIndex = 0
		d.hostIndex++
	}

	if d.hostIndex == len(d.hosts) {
		d.hostIndex = 0
		for i := 0; i < len(d.hosts); i++ {
			d.hosts[i].TickAll(EpochDuration)
		}
	}

	host := &d.hosts[d.hostIndex]

	// Populate host-specific tags:
	p.AppendTag(MachineTagKeys[0], host.Name)
	p.AppendTag(MachineTagKeys[1], host.Region)
	p.AppendTag(MachineTagKeys[2], host.Datacenter)
	p.AppendTag(MachineTagKeys[3], host.Rack)
	p.AppendTag(MachineTagKeys[4], host.OS)
	p.AppendTag(MachineTagKeys[5], host.Arch)
	p.AppendTag(MachineTagKeys[6], host.Team)
	p.AppendTag(MachineTagKeys[7], host.Service)
	p.AppendTag(MachineTagKeys[8], host.ServiceVersion)
	p.AppendTag(MachineTagKeys[9], host.ServiceEnvironment)

	// Populate measurement-specific tags and fields:
	host.SimulatedMeasurements[d.simulatedMeasurementIndex].ToPoint(p)

	d.madePoints++
	d.simulatedMeasurementIndex++
	d.madeValues += int64(len(p.FieldValues))

	return
}
<|endoftext|>