<commit_before>/*
Copyright 2016 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 openstack

import (
	"errors"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
	neutronports "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"

	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

var ErrNoRouterId = errors.New("router-id not set in cloud provider config")

type Routes struct {
	compute *gophercloud.ServiceClient
	network *gophercloud.ServiceClient
	opts    RouterOpts
}

func NewRoutes(compute *gophercloud.ServiceClient, network *gophercloud.ServiceClient, opts RouterOpts) (cloudprovider.Routes, error) {
	if opts.RouterId == "" {
		return nil, ErrNoRouterId
	}

	return &Routes{
		compute: compute,
		network: network,
		opts:    opts,
	}, nil
}

func (r *Routes) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) {
	glog.V(4).Infof("ListRoutes(%v)", clusterName)

	nodeNamesByAddr := make(map[string]types.NodeName)
	err := foreachServer(r.compute, servers.ListOpts{Status: "ACTIVE"}, func(srv *servers.Server) (bool, error) {
		addrs, err := nodeAddresses(srv)
		if err != nil {
			return false, err
		}

		name := mapServerToNodeName(srv)
		for _, addr := range addrs {
			nodeNamesByAddr[addr.Address] = name
		}

		return true, nil
	})
	if err != nil {
		return nil, err
	}

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return nil, err
	}

	var routes []*cloudprovider.Route
	for _, item := range router.Routes {
		nodeName, ok := nodeNamesByAddr[item.NextHop]
		if !ok {
			// Not one of our routes?
			glog.V(4).Infof("Skipping route with unknown nexthop %v", item.NextHop)
			continue
		}
		route := cloudprovider.Route{
			Name:            item.DestinationCIDR,
			TargetNode:      nodeName,
			DestinationCIDR: item.DestinationCIDR,
		}
		routes = append(routes, &route)
	}

	return routes, nil
}

func updateRoutes(network *gophercloud.ServiceClient, router *routers.Router, newRoutes []routers.Route) (func(), error) {
	origRoutes := router.Routes // shallow copy

	_, err := routers.Update(network, router.ID, routers.UpdateOpts{
		Routes: newRoutes,
	}).Extract()
	if err != nil {
		return nil, err
	}

	unwinder := func() {
		glog.V(4).Info("Reverting routes change to router ", router.ID)
		_, err := routers.Update(network, router.ID, routers.UpdateOpts{
			Routes: origRoutes,
		}).Extract()
		if err != nil {
			glog.Warning("Unable to reset routes during error unwind: ", err)
		}
	}

	return unwinder, nil
}

func updateAllowedAddressPairs(network *gophercloud.ServiceClient, port *neutronports.Port, newPairs []neutronports.AddressPair) (func(), error) {
	origPairs := port.AllowedAddressPairs // shallow copy

	_, err := neutronports.Update(network, port.ID, neutronports.UpdateOpts{
		AllowedAddressPairs: newPairs,
	}).Extract()
	if err != nil {
		return nil, err
	}

	unwinder := func() {
		glog.V(4).Info("Reverting allowed-address-pairs change to port ", port.ID)
		_, err := neutronports.Update(network, port.ID, neutronports.UpdateOpts{
			AllowedAddressPairs: origPairs,
		}).Extract()
		if err != nil {
			glog.Warning("Unable to reset allowed-address-pairs during error unwind: ", err)
		}
	}

	return unwinder, nil
}

func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error {
	glog.V(4).Infof("CreateRoute(%v, %v, %v)", clusterName, nameHint, route)

	onFailure := NewCaller()

	addr, err := getAddressByName(r.compute, route.TargetNode)
	if err != nil {
		return err
	}

	glog.V(4).Infof("Using nexthop %v for node %v", addr, route.TargetNode)

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return err
	}

	routes := router.Routes

	for _, item := range routes {
		if item.DestinationCIDR == route.DestinationCIDR && item.NextHop == addr {
			glog.V(4).Infof("Skipping existing route: %v", route)
			return nil
		}
	}

	routes = append(routes, routers.Route{
		DestinationCIDR: route.DestinationCIDR,
		NextHop:         addr,
	})

	unwind, err := updateRoutes(r.network, router, routes)
	if err != nil {
		return err
	}
	defer onFailure.Call(unwind)

	port, err := getPortByIP(r.network, addr)
	if err != nil {
		return err
	}

	found := false
	for _, item := range port.AllowedAddressPairs {
		if item.IPAddress == route.DestinationCIDR {
			glog.V(4).Info("Found existing allowed-address-pair: ", item)
			found = true
			break
		}
	}

	if !found {
		newPairs := append(port.AllowedAddressPairs, neutronports.AddressPair{
			IPAddress: route.DestinationCIDR,
		})
		unwind, err := updateAllowedAddressPairs(r.network, &port, newPairs)
		if err != nil {
			return err
		}
		defer onFailure.Call(unwind)
	}

	glog.V(4).Infof("Route created: %v", route)
	onFailure.Disarm()
	return nil
}

func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
	glog.V(4).Infof("DeleteRoute(%v, %v)", clusterName, route)

	onFailure := NewCaller()

	addr, err := getAddressByName(r.compute, route.TargetNode)
	if err != nil {
		return err
	}

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return err
	}

	routes := router.Routes
	index := -1
	for i, item := range routes {
		if item.DestinationCIDR == route.DestinationCIDR && item.NextHop == addr {
			index = i
			break
		}
	}

	if index == -1 {
		glog.V(4).Infof("Skipping non-existent route: %v", route)
		return nil
	}

	// Delete element `index`
	routes[index] = routes[len(routes)-1]
	routes = routes[:len(routes)-1]

	unwind, err := updateRoutes(r.network, router, routes)
	if err != nil {
		return err
	}
	defer onFailure.Call(unwind)

	port, err := getPortByIP(r.network, addr)
	if err != nil {
		return err
	}

	addr_pairs := port.AllowedAddressPairs
	index = -1
	for i, item := range addr_pairs {
		if item.IPAddress == route.DestinationCIDR {
			index = i
			break
		}
	}

	if index != -1 {
		// Delete element `index`
		addr_pairs[index] = addr_pairs[len(routes)-1]
		addr_pairs = addr_pairs[:len(routes)-1]

		unwind, err := updateAllowedAddressPairs(r.network, &port, addr_pairs)
		if err != nil {
			return err
		}
		defer onFailure.Call(unwind)
	}

	glog.V(4).Infof("Route deleted: %v", route)
	onFailure.Disarm()
	return nil
}
<commit_msg>Fix panic of DeleteRoute()<commit_after>/*
Copyright 2016 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 openstack

import (
	"errors"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
	neutronports "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"

	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

var ErrNoRouterId = errors.New("router-id not set in cloud provider config")

type Routes struct {
	compute *gophercloud.ServiceClient
	network *gophercloud.ServiceClient
	opts    RouterOpts
}

func NewRoutes(compute *gophercloud.ServiceClient, network *gophercloud.ServiceClient, opts RouterOpts) (cloudprovider.Routes, error) {
	if opts.RouterId == "" {
		return nil, ErrNoRouterId
	}

	return &Routes{
		compute: compute,
		network: network,
		opts:    opts,
	}, nil
}

func (r *Routes) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) {
	glog.V(4).Infof("ListRoutes(%v)", clusterName)

	nodeNamesByAddr := make(map[string]types.NodeName)
	err := foreachServer(r.compute, servers.ListOpts{Status: "ACTIVE"}, func(srv *servers.Server) (bool, error) {
		addrs, err := nodeAddresses(srv)
		if err != nil {
			return false, err
		}

		name := mapServerToNodeName(srv)
		for _, addr := range addrs {
			nodeNamesByAddr[addr.Address] = name
		}

		return true, nil
	})
	if err != nil {
		return nil, err
	}

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return nil, err
	}

	var routes []*cloudprovider.Route
	for _, item := range router.Routes {
		nodeName, ok := nodeNamesByAddr[item.NextHop]
		if !ok {
			// Not one of our routes?
			glog.V(4).Infof("Skipping route with unknown nexthop %v", item.NextHop)
			continue
		}
		route := cloudprovider.Route{
			Name:            item.DestinationCIDR,
			TargetNode:      nodeName,
			DestinationCIDR: item.DestinationCIDR,
		}
		routes = append(routes, &route)
	}

	return routes, nil
}

func updateRoutes(network *gophercloud.ServiceClient, router *routers.Router, newRoutes []routers.Route) (func(), error) {
	origRoutes := router.Routes // shallow copy

	_, err := routers.Update(network, router.ID, routers.UpdateOpts{
		Routes: newRoutes,
	}).Extract()
	if err != nil {
		return nil, err
	}

	unwinder := func() {
		glog.V(4).Info("Reverting routes change to router ", router.ID)
		_, err := routers.Update(network, router.ID, routers.UpdateOpts{
			Routes: origRoutes,
		}).Extract()
		if err != nil {
			glog.Warning("Unable to reset routes during error unwind: ", err)
		}
	}

	return unwinder, nil
}

func updateAllowedAddressPairs(network *gophercloud.ServiceClient, port *neutronports.Port, newPairs []neutronports.AddressPair) (func(), error) {
	origPairs := port.AllowedAddressPairs // shallow copy

	_, err := neutronports.Update(network, port.ID, neutronports.UpdateOpts{
		AllowedAddressPairs: newPairs,
	}).Extract()
	if err != nil {
		return nil, err
	}

	unwinder := func() {
		glog.V(4).Info("Reverting allowed-address-pairs change to port ", port.ID)
		_, err := neutronports.Update(network, port.ID, neutronports.UpdateOpts{
			AllowedAddressPairs: origPairs,
		}).Extract()
		if err != nil {
			glog.Warning("Unable to reset allowed-address-pairs during error unwind: ", err)
		}
	}

	return unwinder, nil
}

func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error {
	glog.V(4).Infof("CreateRoute(%v, %v, %v)", clusterName, nameHint, route)

	onFailure := NewCaller()

	addr, err := getAddressByName(r.compute, route.TargetNode)
	if err != nil {
		return err
	}

	glog.V(4).Infof("Using nexthop %v for node %v", addr, route.TargetNode)

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return err
	}

	routes := router.Routes

	for _, item := range routes {
		if item.DestinationCIDR == route.DestinationCIDR && item.NextHop == addr {
			glog.V(4).Infof("Skipping existing route: %v", route)
			return nil
		}
	}

	routes = append(routes, routers.Route{
		DestinationCIDR: route.DestinationCIDR,
		NextHop:         addr,
	})

	unwind, err := updateRoutes(r.network, router, routes)
	if err != nil {
		return err
	}
	defer onFailure.Call(unwind)

	port, err := getPortByIP(r.network, addr)
	if err != nil {
		return err
	}

	found := false
	for _, item := range port.AllowedAddressPairs {
		if item.IPAddress == route.DestinationCIDR {
			glog.V(4).Info("Found existing allowed-address-pair: ", item)
			found = true
			break
		}
	}

	if !found {
		newPairs := append(port.AllowedAddressPairs, neutronports.AddressPair{
			IPAddress: route.DestinationCIDR,
		})
		unwind, err := updateAllowedAddressPairs(r.network, &port, newPairs)
		if err != nil {
			return err
		}
		defer onFailure.Call(unwind)
	}

	glog.V(4).Infof("Route created: %v", route)
	onFailure.Disarm()
	return nil
}

func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
	glog.V(4).Infof("DeleteRoute(%v, %v)", clusterName, route)

	onFailure := NewCaller()

	addr, err := getAddressByName(r.compute, route.TargetNode)
	if err != nil {
		return err
	}

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return err
	}

	routes := router.Routes
	index := -1
	for i, item := range routes {
		if item.DestinationCIDR == route.DestinationCIDR && item.NextHop == addr {
			index = i
			break
		}
	}

	if index == -1 {
		glog.V(4).Infof("Skipping non-existent route: %v", route)
		return nil
	}

	// Delete element `index`
	routes[index] = routes[len(routes)-1]
	routes = routes[:len(routes)-1]

	unwind, err := updateRoutes(r.network, router, routes)
	if err != nil {
		return err
	}
	defer onFailure.Call(unwind)

	port, err := getPortByIP(r.network, addr)
	if err != nil {
		return err
	}

	addr_pairs := port.AllowedAddressPairs
	index = -1
	for i, item := range addr_pairs {
		if item.IPAddress == route.DestinationCIDR {
			index = i
			break
		}
	}

	if index != -1 {
		// Delete element `index`
		addr_pairs[index] = addr_pairs[len(addr_pairs)-1]
		addr_pairs = addr_pairs[:len(addr_pairs)-1]

		unwind, err := updateAllowedAddressPairs(r.network, &port, addr_pairs)
		if err != nil {
			return err
		}
		defer onFailure.Call(unwind)
	}

	glog.V(4).Infof("Route deleted: %v", route)
	onFailure.Disarm()
	return nil
}
<|endoftext|>


<commit_before>package clusterdeploy

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/rancher/norman/types"
	util "github.com/rancher/rancher/pkg/cluster"
	"github.com/rancher/rancher/pkg/clustermanager"
	"github.com/rancher/rancher/pkg/features"
	"github.com/rancher/rancher/pkg/image"
	"github.com/rancher/rancher/pkg/kubectl"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/rancher/pkg/systemaccount"
	"github.com/rancher/rancher/pkg/systemtemplate"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/rancher/types/user"
	"github.com/sirupsen/logrus"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

const (
	AgentForceDeployAnn = "io.cattle.agent.force.deploy"
	nodeImage           = "nodeImage"
	clusterImage        = "clusterImage"
)

var (
	agentImagesMutex sync.RWMutex
	agentImages      = map[string]map[string]string{
		nodeImage:    map[string]string{},
		clusterImage: map[string]string{},
	}
)

func Register(ctx context.Context, management *config.ManagementContext, clusterManager *clustermanager.Manager) {
	c := &clusterDeploy{
		systemAccountManager: systemaccount.NewManager(management),
		userManager:          management.UserManager,
		clusters:             management.Management.Clusters(""),
		nodeLister:           management.Management.Nodes("").Controller().Lister(),
		clusterManager:       clusterManager,
	}

	management.Management.Clusters("").AddHandler(ctx, "cluster-deploy", c.sync)
}

type clusterDeploy struct {
	systemAccountManager *systemaccount.Manager
	userManager          user.Manager
	clusters             v3.ClusterInterface
	clusterManager       *clustermanager.Manager
	nodeLister           v3.NodeLister
}

func (cd *clusterDeploy) sync(key string, cluster *v3.Cluster) (runtime.Object, error) {
	var (
		err, updateErr error
	)

	if cluster == nil || cluster.DeletionTimestamp != nil {
		// remove the system account user created for this cluster
		if err := cd.systemAccountManager.RemoveSystemAccount(key); err != nil {
			return nil, err
		}
		return nil, nil
	}

	original := cluster
	cluster = original.DeepCopy()

	if cluster.Status.Driver == v3.ClusterDriverRKE {
		if cluster.Spec.LocalClusterAuthEndpoint.Enabled {
			cluster.Spec.RancherKubernetesEngineConfig.Authentication.Strategy = "x509|webhook"
		} else {
			cluster.Spec.RancherKubernetesEngineConfig.Authentication.Strategy = "x509"
		}
	}

	err = cd.doSync(cluster)
	if cluster != nil && !reflect.DeepEqual(cluster, original) {
		_, updateErr = cd.clusters.Update(cluster)
	}

	if err != nil {
		return nil, err
	}
	return nil, updateErr
}

func (cd *clusterDeploy) doSync(cluster *v3.Cluster) error {
	if !v3.ClusterConditionProvisioned.IsTrue(cluster) {
		return nil
	}

	nodes, err := cd.nodeLister.List(cluster.Name, labels.Everything())
	if err != nil {
		return err
	}
	if len(nodes) == 0 {
		return nil
	}

	_, err = v3.ClusterConditionSystemAccountCreated.DoUntilTrue(cluster, func() (runtime.Object, error) {
		return cluster, cd.systemAccountManager.CreateSystemAccount(cluster)
	})
	if err != nil {
		return err
	}

	if cluster.Status.AgentImage != "" && !agentImagesCached(cluster.Name) {
		if err := cd.cacheAgentImages(cluster.Name); err != nil {
			return err
		}
	}

	err = cd.deployAgent(cluster)
	if err != nil {
		return err
	}

	return cd.setNetworkPolicyAnn(cluster)
}

// agentFeaturesChanged will treat a missing key as false. This means we only detect changes
// when we set a feature to true so we can't reliably set a feature to false that is enabled by default.
// This behavior makes adding new def false features not cause the agent to redeploy.
func agentFeaturesChanged(desired, actual map[string]bool) bool {
	for k, v := range desired {
		if actual[k] != v {
			return true
		}
	}

	for k, v := range actual {
		if desired[k] != v {
			return true
		}
	}

	return false
}

func redeployAgent(cluster *v3.Cluster, desiredAgent, desiredAuth string, desiredFeatures map[string]bool) bool {
	forceDeploy := cluster.Annotations[AgentForceDeployAnn] == "true"
	imageChange := cluster.Status.AgentImage != desiredAgent || cluster.Status.AuthImage != desiredAuth
	agentFeaturesChanged := agentFeaturesChanged(desiredFeatures, cluster.Status.AgentFeatures)
	repoChange := false
	if cluster.Spec.RancherKubernetesEngineConfig != nil {
		if cluster.Status.AppliedSpec.RancherKubernetesEngineConfig != nil {
			desiredRepo := util.GetPrivateRepo(cluster)
			var appliedRepo *v3.PrivateRegistry
			if len(cluster.Status.AppliedSpec.RancherKubernetesEngineConfig.PrivateRegistries) > 0 {
				appliedRepo = &cluster.Status.AppliedSpec.RancherKubernetesEngineConfig.PrivateRegistries[0]
			}
			if desiredRepo != nil && appliedRepo != nil && !reflect.DeepEqual(desiredRepo, appliedRepo) {
				repoChange = true
			}
			if (desiredRepo == nil && appliedRepo != nil) || (desiredRepo != nil && appliedRepo == nil) {
				repoChange = true
			}
		}
	}

	if forceDeploy || imageChange || repoChange || agentFeaturesChanged {
		logrus.Infof("Redeploy Rancher Agents is needed for %s: forceDeploy=%v, agent/auth image changed=%v,"+
			" private repo changed=%v, agent features changed=%v", cluster.Name, forceDeploy, imageChange, repoChange,
			agentFeaturesChanged)
		return true
	}

	na, ca := getAgentImages(cluster.Name)
	if cluster.Status.AgentImage != na || cluster.Status.AgentImage != ca {
		// downstream agent does not match, kick a redeploy with settings agent
		logrus.Infof("Redeploy Rancher Agents due to Downstream Agent Image Mismatch for %s: was %s and will be %s",
			cluster.Name, na, image.ResolveWithCluster(settings.AgentImage.Get(), cluster))
		clearAgentImages(cluster.Name)
		return true
	}

	return false
}

func getDesiredImage(cluster *v3.Cluster) string {
	if cluster.Spec.AgentImageOverride != "" {
		return cluster.Spec.AgentImageOverride
	}

	return cluster.Spec.DesiredAgentImage
}

func (cd *clusterDeploy) deployAgent(cluster *v3.Cluster) error {
	desiredAgent := getDesiredImage(cluster)
	if desiredAgent == "" || desiredAgent == "fixed" {
		desiredAgent = image.ResolveWithCluster(settings.AgentImage.Get(), cluster)
	}

	var desiredAuth string
	if cluster.Spec.LocalClusterAuthEndpoint.Enabled {
		desiredAuth = cluster.Spec.DesiredAuthImage
		if desiredAuth == "" || desiredAuth == "fixed" {
			desiredAuth = image.ResolveWithCluster(settings.AuthImage.Get(), cluster)
		}
	}

	desiredFeatures := map[string]bool{
		features.Steve.Name(): features.Steve.Enabled(),
	}

	if !redeployAgent(cluster, desiredAgent, desiredAuth, desiredFeatures) {
		return nil
	}

	kubeConfig, err := cd.getKubeConfig(cluster)
	if err != nil {
		return err
	}

	if _, err = v3.ClusterConditionAgentDeployed.Do(cluster, func() (runtime.Object, error) {
		yaml, err := cd.getYAML(cluster, desiredAgent, desiredAuth, desiredFeatures)
		if err != nil {
			return cluster, err
		}
		var output []byte
		for i := 0; i < 3; i++ {
			// This will fail almost always the first time because when we create the namespace in the file
			// it won't have privileges.  Just stupidly try 3 times
			output, err = kubectl.Apply(yaml, kubeConfig)
			if err == nil {
				break
			}
			time.Sleep(2 * time.Second)
		}
		if err != nil {
			return cluster, types.NewErrors(err, errors.New(string(output)))
		}
		v3.ClusterConditionAgentDeployed.Message(cluster, string(output))
		if !cluster.Spec.LocalClusterAuthEndpoint.Enabled && cluster.Status.AppliedSpec.LocalClusterAuthEndpoint.Enabled && cluster.Status.AuthImage != "" {
			output, err = kubectl.Delete([]byte(systemtemplate.AuthDaemonSet), kubeConfig)
		}
		if err != nil {
			return cluster, types.NewErrors(err, errors.New(string(output)))
		}
		v3.ClusterConditionAgentDeployed.Message(cluster, string(output))
		return cluster, nil
	}); err != nil {
		return err
	}

	if err = cd.cacheAgentImages(cluster.Name); err != nil {
		return err
	}

	cluster.Status.AgentImage = desiredAgent
	cluster.Status.AgentFeatures = desiredFeatures
	if cluster.Spec.DesiredAgentImage == "fixed" {
		cluster.Spec.DesiredAgentImage = desiredAgent
	}
	cluster.Status.AuthImage = desiredAuth
	if cluster.Spec.DesiredAuthImage == "fixed" {
		cluster.Spec.DesiredAuthImage = desiredAuth
	}
	if cluster.Annotations[AgentForceDeployAnn] == "true" {
		cluster.Annotations[AgentForceDeployAnn] = "false"
	}

	return nil
}

func (cd *clusterDeploy) setNetworkPolicyAnn(cluster *v3.Cluster) error {
	if cluster.Spec.EnableNetworkPolicy != nil {
		return nil
	}
	// set current state for upgraded canal clusters
	if cluster.Spec.RancherKubernetesEngineConfig != nil &&
		cluster.Spec.RancherKubernetesEngineConfig.Network.Plugin == "canal" {
		enableNetworkPolicy := true
		cluster.Spec.EnableNetworkPolicy = &enableNetworkPolicy
		cluster.Annotations["networking.management.cattle.io/enable-network-policy"] = "true"
	}
	return nil
}

func (cd *clusterDeploy) getKubeConfig(cluster *v3.Cluster) (*clientcmdapi.Config, error) {
	user, err := cd.systemAccountManager.GetSystemUser(cluster.Name)
	if err != nil {
		return nil, err
	}

	token, err := cd.userManager.EnsureToken("agent-"+user.Name, "token for agent deployment", "agent", user.Name)
	if err != nil {
		return nil, err
	}

	return cd.clusterManager.KubeConfig(cluster.Name, token), nil
}

func (cd *clusterDeploy) getYAML(cluster *v3.Cluster, agentImage, authImage string, features map[string]bool) ([]byte, error) {
	logrus.Debug("Desired agent image:", agentImage)
	logrus.Debug("Desired auth image:", authImage)

	token, err := cd.systemAccountManager.GetOrCreateSystemClusterToken(cluster.Name)
	if err != nil {
		return nil, err
	}

	url := settings.ServerURL.Get()
	if url == "" {
		return nil, fmt.Errorf("waiting for server-url setting to be set")
	}

	buf := &bytes.Buffer{}
	err = systemtemplate.SystemTemplate(buf, agentImage, authImage, cluster.Name, token, url, cluster.Spec.WindowsPreferedCluster,
		cluster, features)

	return buf.Bytes(), err
}

func (cd *clusterDeploy) getClusterAgentImage(name string) (string, error) {
	uc, err := cd.clusterManager.UserContext(name)
	if err != nil {
		return "", err
	}

	d, err := uc.Apps.Deployments("cattle-system").Get("cattle-cluster-agent", v1.GetOptions{})
	if err != nil {
		if !apierrors.IsNotFound(err) {
			return "", err
		}
		return "", nil
	}

	for _, c := range d.Spec.Template.Spec.Containers {
		if c.Name == "cluster-register" {
			return c.Image, nil
		}
	}

	return "", nil
}

func (cd *clusterDeploy) getNodeAgentImage(name string) (string, error) {
	uc, err := cd.clusterManager.UserContext(name)
	if err != nil {
		return "", err
	}

	ds, err := uc.Apps.DaemonSets("cattle-system").Get("cattle-node-agent", v1.GetOptions{})
	if err != nil {
		if !apierrors.IsNotFound(err) {
			return "", err
		}
		return "", nil
	}

	for _, c := range ds.Spec.Template.Spec.Containers {
		if c.Name == "agent" {
			return c.Image, nil
		}
	}

	return "", nil
}

func (cd *clusterDeploy) cacheAgentImages(name string) error {
	na, err := cd.getNodeAgentImage(name)
	if err != nil {
		return err
	}

	ca, err := cd.getClusterAgentImage(name)
	if err != nil {
		return err
	}

	agentImagesMutex.Lock()
	defer agentImagesMutex.Unlock()
	agentImages[nodeImage][name] = na
	agentImages[clusterImage][name] = ca
	return nil
}

func agentImagesCached(name string) bool {
	na, ca := getAgentImages(name)
	return na != "" && ca != ""
}

func getAgentImages(name string) (string, string) {
	agentImagesMutex.RLock()
	defer agentImagesMutex.RUnlock()
	return agentImages[nodeImage][name], agentImages[clusterImage][name]
}

func clearAgentImages(name string) {
	agentImagesMutex.Lock()
	defer agentImagesMutex.Unlock()
	delete(agentImages[nodeImage], name)
	delete(agentImages[clusterImage], name)
}
<commit_msg>Always redeploy agent if agent deployment didn't succeed<commit_after>package clusterdeploy

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/rancher/norman/types"
	util "github.com/rancher/rancher/pkg/cluster"
	"github.com/rancher/rancher/pkg/clustermanager"
	"github.com/rancher/rancher/pkg/features"
	"github.com/rancher/rancher/pkg/image"
	"github.com/rancher/rancher/pkg/kubectl"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/rancher/pkg/systemaccount"
	"github.com/rancher/rancher/pkg/systemtemplate"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/rancher/types/user"
	"github.com/sirupsen/logrus"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

const (
	AgentForceDeployAnn = "io.cattle.agent.force.deploy"
	nodeImage           = "nodeImage"
	clusterImage        = "clusterImage"
)

var (
	agentImagesMutex sync.RWMutex
	agentImages      = map[string]map[string]string{
		nodeImage:    map[string]string{},
		clusterImage: map[string]string{},
	}
)

func Register(ctx context.Context, management *config.ManagementContext, clusterManager *clustermanager.Manager) {
	c := &clusterDeploy{
		systemAccountManager: systemaccount.NewManager(management),
		userManager:          management.UserManager,
		clusters:             management.Management.Clusters(""),
		nodeLister:           management.Management.Nodes("").Controller().Lister(),
		clusterManager:       clusterManager,
	}

	management.Management.Clusters("").AddHandler(ctx, "cluster-deploy", c.sync)
}

type clusterDeploy struct {
	systemAccountManager *systemaccount.Manager
	userManager          user.Manager
	clusters             v3.ClusterInterface
	clusterManager       *clustermanager.Manager
	nodeLister           v3.NodeLister
}

func (cd *clusterDeploy) sync(key string, cluster *v3.Cluster) (runtime.Object, error) {
	var (
		err, updateErr error
	)

	if cluster == nil || cluster.DeletionTimestamp != nil {
		// remove the system account user created for this cluster
		if err := cd.systemAccountManager.RemoveSystemAccount(key); err != nil {
			return nil, err
		}
		return nil, nil
	}

	original := cluster
	cluster = original.DeepCopy()

	if cluster.Status.Driver == v3.ClusterDriverRKE {
		if cluster.Spec.LocalClusterAuthEndpoint.Enabled {
			cluster.Spec.RancherKubernetesEngineConfig.Authentication.Strategy = "x509|webhook"
		} else {
			cluster.Spec.RancherKubernetesEngineConfig.Authentication.Strategy = "x509"
		}
	}

	err = cd.doSync(cluster)
	if cluster != nil && !reflect.DeepEqual(cluster, original) {
		_, updateErr = cd.clusters.Update(cluster)
	}

	if err != nil {
		return nil, err
	}
	return nil, updateErr
}

func (cd *clusterDeploy) doSync(cluster *v3.Cluster) error {
	if !v3.ClusterConditionProvisioned.IsTrue(cluster) {
		return nil
	}

	nodes, err := cd.nodeLister.List(cluster.Name, labels.Everything())
	if err != nil {
		return err
	}
	if len(nodes) == 0 {
		return nil
	}

	_, err = v3.ClusterConditionSystemAccountCreated.DoUntilTrue(cluster, func() (runtime.Object, error) {
		return cluster, cd.systemAccountManager.CreateSystemAccount(cluster)
	})
	if err != nil {
		return err
	}

	if cluster.Status.AgentImage != "" && !agentImagesCached(cluster.Name) {
		if err := cd.cacheAgentImages(cluster.Name); err != nil {
			return err
		}
	}

	err = cd.deployAgent(cluster)
	if err != nil {
		return err
	}

	return cd.setNetworkPolicyAnn(cluster)
}

// agentFeaturesChanged will treat a missing key as false. This means we only detect changes
// when we set a feature to true so we can't reliably set a feature to false that is enabled by default.
// This behavior makes adding new def false features not cause the agent to redeploy.
func agentFeaturesChanged(desired, actual map[string]bool) bool {
	for k, v := range desired {
		if actual[k] != v {
			return true
		}
	}

	for k, v := range actual {
		if desired[k] != v {
			return true
		}
	}

	return false
}

func redeployAgent(cluster *v3.Cluster, desiredAgent, desiredAuth string, desiredFeatures map[string]bool) bool {
	if !v3.ClusterConditionAgentDeployed.IsTrue(cluster) {
		return true
	}

	forceDeploy := cluster.Annotations[AgentForceDeployAnn] == "true"
	imageChange := cluster.Status.AgentImage != desiredAgent || cluster.Status.AuthImage != desiredAuth
	agentFeaturesChanged := agentFeaturesChanged(desiredFeatures, cluster.Status.AgentFeatures)
	repoChange := false
	if cluster.Spec.RancherKubernetesEngineConfig != nil {
		if cluster.Status.AppliedSpec.RancherKubernetesEngineConfig != nil {
			desiredRepo := util.GetPrivateRepo(cluster)
			var appliedRepo *v3.PrivateRegistry
			if len(cluster.Status.AppliedSpec.RancherKubernetesEngineConfig.PrivateRegistries) > 0 {
				appliedRepo = &cluster.Status.AppliedSpec.RancherKubernetesEngineConfig.PrivateRegistries[0]
			}
			if desiredRepo != nil && appliedRepo != nil && !reflect.DeepEqual(desiredRepo, appliedRepo) {
				repoChange = true
			}
			if (desiredRepo == nil && appliedRepo != nil) || (desiredRepo != nil && appliedRepo == nil) {
				repoChange = true
			}
		}
	}

	if forceDeploy || imageChange || repoChange || agentFeaturesChanged {
		logrus.Infof("Redeploy Rancher Agents is needed for %s: forceDeploy=%v, agent/auth image changed=%v,"+
			" private repo changed=%v, agent features changed=%v", cluster.Name, forceDeploy, imageChange, repoChange,
			agentFeaturesChanged)
		return true
	}

	na, ca := getAgentImages(cluster.Name)
	if cluster.Status.AgentImage != na || cluster.Status.AgentImage != ca {
		// downstream agent does not match, kick a redeploy with settings agent
		logrus.Infof("Redeploy Rancher Agents due to Downstream Agent Image Mismatch for %s: was %s and will be %s",
			cluster.Name, na, image.ResolveWithCluster(settings.AgentImage.Get(), cluster))
		clearAgentImages(cluster.Name)
		return true
	}

	return false
}

func getDesiredImage(cluster *v3.Cluster) string {
	if cluster.Spec.AgentImageOverride != "" {
		return cluster.Spec.AgentImageOverride
	}

	return cluster.Spec.DesiredAgentImage
}

func (cd *clusterDeploy) deployAgent(cluster *v3.Cluster) error {
	desiredAgent := getDesiredImage(cluster)
	if desiredAgent == "" || desiredAgent == "fixed" {
		desiredAgent = image.ResolveWithCluster(settings.AgentImage.Get(), cluster)
	}

	var desiredAuth string
	if cluster.Spec.LocalClusterAuthEndpoint.Enabled {
		desiredAuth = cluster.Spec.DesiredAuthImage
		if desiredAuth == "" || desiredAuth == "fixed" {
			desiredAuth = image.ResolveWithCluster(settings.AuthImage.Get(), cluster)
		}
	}

	desiredFeatures := map[string]bool{
		features.Steve.Name(): features.Steve.Enabled(),
	}

	if !redeployAgent(cluster, desiredAgent, desiredAuth, desiredFeatures) {
		return nil
	}

	kubeConfig, err := cd.getKubeConfig(cluster)
	if err != nil {
		return err
	}

	if _, err = v3.ClusterConditionAgentDeployed.Do(cluster, func() (runtime.Object, error) {
		yaml, err := cd.getYAML(cluster, desiredAgent, desiredAuth, desiredFeatures)
		if err != nil {
			return cluster, err
		}
		var output []byte
		for i := 0; i < 3; i++ {
			// This will fail almost always the first time because when we create the namespace in the file
			// it won't have privileges.  Just stupidly try 3 times
			output, err = kubectl.Apply(yaml, kubeConfig)
			if err == nil {
				break
			}
			time.Sleep(2 * time.Second)
		}
		if err != nil {
			return cluster, types.NewErrors(err, errors.New(string(output)))
		}
		v3.ClusterConditionAgentDeployed.Message(cluster, string(output))
		if !cluster.Spec.LocalClusterAuthEndpoint.Enabled && cluster.Status.AppliedSpec.LocalClusterAuthEndpoint.Enabled && cluster.Status.AuthImage != "" {
			output, err = kubectl.Delete([]byte(systemtemplate.AuthDaemonSet), kubeConfig)
		}
		if err != nil {
			return cluster, types.NewErrors(err, errors.New(string(output)))
		}
		v3.ClusterConditionAgentDeployed.Message(cluster, string(output))
		return cluster, nil
	}); err != nil {
		return err
	}

	if err = cd.cacheAgentImages(cluster.Name); err != nil {
		return err
	}

	cluster.Status.AgentImage = desiredAgent
	cluster.Status.AgentFeatures = desiredFeatures
	if cluster.Spec.DesiredAgentImage == "fixed" {
		cluster.Spec.DesiredAgentImage = desiredAgent
	}
	cluster.Status.AuthImage = desiredAuth
	if cluster.Spec.DesiredAuthImage == "fixed" {
		cluster.Spec.DesiredAuthImage = desiredAuth
	}
	if cluster.Annotations[AgentForceDeployAnn] == "true" {
		cluster.Annotations[AgentForceDeployAnn] = "false"
	}

	return nil
}

func (cd *clusterDeploy) setNetworkPolicyAnn(cluster *v3.Cluster) error {
	if cluster.Spec.EnableNetworkPolicy != nil {
		return nil
	}
	// set current state for upgraded canal clusters
	if cluster.Spec.RancherKubernetesEngineConfig != nil &&
		cluster.Spec.RancherKubernetesEngineConfig.Network.Plugin == "canal" {
		enableNetworkPolicy := true
		cluster.Spec.EnableNetworkPolicy = &enableNetworkPolicy
		cluster.Annotations["networking.management.cattle.io/enable-network-policy"] = "true"
	}
	return nil
}

func (cd *clusterDeploy) getKubeConfig(cluster *v3.Cluster) (*clientcmdapi.Config, error) {
	user, err := cd.systemAccountManager.GetSystemUser(cluster.Name)
	if err != nil {
		return nil, err
	}

	token, err := cd.userManager.EnsureToken("agent-"+user.Name, "token for agent deployment", "agent", user.Name)
	if err != nil {
		return nil, err
	}

	return cd.clusterManager.KubeConfig(cluster.Name, token), nil
}

func (cd *clusterDeploy) getYAML(cluster *v3.Cluster, agentImage, authImage string, features map[string]bool) ([]byte, error) {
	logrus.Debug("Desired agent image:", agentImage)
	logrus.Debug("Desired auth image:", authImage)

	token, err := cd.systemAccountManager.GetOrCreateSystemClusterToken(cluster.Name)
	if err != nil {
		return nil, err
	}

	url := settings.ServerURL.Get()
	if url == "" {
		return nil, fmt.Errorf("waiting for server-url setting to be set")
	}

	buf := &bytes.Buffer{}
	err = systemtemplate.SystemTemplate(buf, agentImage, authImage, cluster.Name, token, url, cluster.Spec.WindowsPreferedCluster,
		cluster, features)

	return buf.Bytes(), err
}

func (cd *clusterDeploy) getClusterAgentImage(name string) (string, error) {
	uc, err := cd.clusterManager.UserContext(name)
	if err != nil {
		return "", err
	}

	d, err := uc.Apps.Deployments("cattle-system").Get("cattle-cluster-agent", v1.GetOptions{})
	if err != nil {
		if !apierrors.IsNotFound(err) {
			return "", err
		}
		return "", nil
	}

	for _, c := range d.Spec.Template.Spec.Containers {
		if c.Name == "cluster-register" {
			return c.Image, nil
		}
	}

	return "", nil
}

func (cd *clusterDeploy) getNodeAgentImage(name string) (string, error) {
	uc, err := cd.clusterManager.UserContext(name)
	if err != nil {
		return "", err
	}

	ds, err := uc.Apps.DaemonSets("cattle-system").Get("cattle-node-agent", v1.GetOptions{})
	if err != nil {
		if !apierrors.IsNotFound(err) {
			return "", err
		}
		return "", nil
	}

	for _, c := range ds.Spec.Template.Spec.Containers {
		if c.Name == "agent" {
			return c.Image, nil
		}
	}

	return "", nil
}

func (cd *clusterDeploy) cacheAgentImages(name string) error {
	na, err := cd.getNodeAgentImage(name)
	if err != nil {
		return err
	}

	ca, err := cd.getClusterAgentImage(name)
	if err != nil {
		return err
	}

	agentImagesMutex.Lock()
	defer agentImagesMutex.Unlock()
	agentImages[nodeImage][name] = na
	agentImages[clusterImage][name] = ca
	return nil
}

func agentImagesCached(name string) bool {
	na, ca := getAgentImages(name)
	return na != "" && ca != ""
}

func getAgentImages(name string) (string, string) {
	agentImagesMutex.RLock()
	defer agentImagesMutex.RUnlock()
	return agentImages[nodeImage][name], agentImages[clusterImage][name]
}

func clearAgentImages(name string) {
	agentImagesMutex.Lock()
	defer agentImagesMutex.Unlock()
	delete(agentImages[nodeImage], name)
	delete(agentImages[clusterImage], name)
}
<|endoftext|>


<commit_before>package resourcequota

import (
	"encoding/json"
	"reflect"
	"sync"
	"time"

	namespaceutil "github.com/rancher/rancher/pkg/namespace"
	validate "github.com/rancher/rancher/pkg/resourcequota"
	"github.com/rancher/types/apis/core/v1"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/cache"
	clientcache "k8s.io/client-go/tools/cache"
)

const (
	projectIDAnnotation             = "field.cattle.io/projectId"
	resourceQuotaLabel              = "resourcequota.management.cattle.io/default-resource-quota"
	resourceQuotaInitCondition      = "ResourceQuotaInit"
	resourceQuotaAnnotation         = "field.cattle.io/resourceQuota"
	resourceQuotaValidatedCondition = "ResourceQuotaValidated"
)

var (
	projectLockCache = cache.NewLRUExpireCache(1000)
)

/*
SyncController takes care of creating Kubernetes resource quota based on the resource limits
defined in namespace.resourceQuota
*/
type SyncController struct {
	ProjectLister       v3.ProjectLister
	Namespaces          v1.NamespaceInterface
	NamespaceLister     v1.NamespaceLister
	ResourceQuotas      v1.ResourceQuotaInterface
	ResourceQuotaLister v1.ResourceQuotaLister
	NsIndexer           clientcache.Indexer
}

func (c *SyncController) syncResourceQuota(key string, ns *corev1.Namespace) error {
	if ns == nil || ns.DeletionTimestamp != nil {
		return nil
	}

	_, err := c.CreateResourceQuota(ns)
	return err
}

func (c *SyncController) CreateResourceQuota(ns *corev1.Namespace) (*corev1.Namespace, error) {
	existing, err := c.getExistingResourceQuota(ns)
	if err != nil {
		return ns, err
	}

	projectLimit, _, err := getProjectResourceQuotaLimit(ns, c.ProjectLister)
	if err != nil {
		return ns, err
	}

	quotaSpec, err := c.getNamespaceResourceQuota(ns, projectLimit != nil)
	if err != nil {
		return ns, err
	}

	operation := "none"
	if existing == nil {
		if quotaSpec != nil {
			operation = "create"
		}
	} else {
		if quotaSpec == nil {
			operation = "delete"
		} else if !reflect.DeepEqual(existing.Spec.Hard, quotaSpec.Hard) {
			operation = "update"
		}
	}

	switch operation {
	case "create":
		isFit, err := c.validateNamespaceQuota(ns)
		if err != nil || !isFit {
			return ns, err
		}
		err = c.createDefaultResourceQuota(ns, quotaSpec)
	case "update":
		isFit, err := c.validateNamespaceQuota(ns)
		if err != nil || !isFit {
			return ns, err
		}
		err = c.updateResourceQuota(existing, quotaSpec)
	case "delete":
		err = c.deleteResourceQuota(existing)
	}
	if err == nil {
		set, err := namespaceutil.IsNamespaceConditionSet(ns, resourceQuotaInitCondition, true)
		if err != nil || set {
			return ns, err
		}
		toUpdate, err := c.Namespaces.Get(ns.Name, metav1.GetOptions{})
		if err != nil {
			return toUpdate, err
		}
		namespaceutil.SetNamespaceCondition(toUpdate, time.Second*1, resourceQuotaInitCondition, true, "")
		return c.Namespaces.Update(toUpdate)
	}

	return ns, err
}

func (c *SyncController) updateResourceQuota(quota *corev1.ResourceQuota, spec *corev1.ResourceQuotaSpec) error {
	toUpdate := quota.DeepCopy()
	toUpdate.Spec = *spec
	logrus.Infof("Updating default resource quota for namespace %v", toUpdate.Namespace)
	_, err := c.ResourceQuotas.Update(toUpdate)
	return err
}

func (c *SyncController) deleteResourceQuota(quota *corev1.ResourceQuota) error {
	logrus.Infof("Deleting default resource quota for namespace %v", quota.Namespace)
	return c.ResourceQuotas.DeleteNamespaced(quota.Namespace, quota.Name, &metav1.DeleteOptions{})
}

func (c *SyncController) getExistingResourceQuota(ns *corev1.Namespace) (*corev1.ResourceQuota, error) {
	set := labels.Set(map[string]string{resourceQuotaLabel: "true"})
	quota, err := c.ResourceQuotaLister.List(ns.Name, set.AsSelector())
	if err != nil {
		return nil, err
	}
	if len(quota) == 0 {
		return nil, nil
	}
	return quota[0], nil
}

func (c *SyncController) getNamespaceResourceQuota(ns *corev1.Namespace, setDefault bool) (*corev1.ResourceQuotaSpec, error) {
	limit, err := getNamespaceLimit(ns)
	if err != nil {
		return nil, err
	}
	if limit == nil {
		if setDefault {
			limit = defaultResourceLimit
		} else {
			return nil, nil
		}
	}

	return convertResourceLimitResourceQuotaSpec(limit)
}

var defaultResourceLimit = &v3.ResourceQuotaLimit{
	Pods:                   "0",
	Services:               "0",
	ReplicationControllers: "0",
	Secrets:                "0",
	ConfigMaps:             "0",
	PersistentVolumeClaims: "0",
	ServicesNodePorts:      "0",
	ServicesLoadBalancers:  "0",
	RequestsCPU:            "0",
	RequestsMemory:         "0",
	RequestsStorage:        "0",
	LimitsCPU:              "0",
	LimitsMemory:           "0",
}

func (c *SyncController) createDefaultResourceQuota(ns *corev1.Namespace, spec *corev1.ResourceQuotaSpec) error {
	resourceQuota := &corev1.ResourceQuota{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "default-",
			Namespace:    ns.Name,
			Labels:       map[string]string{resourceQuotaLabel: "true"},
		},
		Spec: *spec,
	}
	logrus.Infof("Creating default resource quota for namespace %v", ns.Name)
	_, err := c.ResourceQuotas.Create(resourceQuota)
	return err
}

func (c *SyncController) validateNamespaceQuota(ns *corev1.Namespace) (bool, error) {
	if ns == nil || ns.DeletionTimestamp != nil {
		return true, nil
	}

	// get project limit
	projectLimit, projectID, err := getProjectResourceQuotaLimit(ns, c.ProjectLister)
	if err != nil {
		return false, err
	}

	if projectLimit == nil {
		return true, err
	}

	// set default quota if not set
	quotaToUpdate, err := c.getResourceQuotaToUpdate(ns)
	if err != nil {
		return false, err
	}
	finalNs := *ns
	if quotaToUpdate != "" {
		toUpdate := ns.DeepCopy()
		if toUpdate.Annotations == nil {
			toUpdate.Annotations = map[string]string{}
		}
		toUpdate.Annotations[resourceQuotaAnnotation] = quotaToUpdate
		toUpdate, err = c.Namespaces.Update(toUpdate)
		if err != nil {
			return false, err
		}
		finalNs = *toUpdate
	}

	// validate resource quota
	mu := getProjectLock(projectID)
	mu.Lock()
	defer mu.Unlock()
	// get other Namespaces
	objects, err := c.NsIndexer.ByIndex(nsByProjectIndex, projectID)
	if err != nil {
		return false, err
	}
	var nsLimits []*v3.ResourceQuotaLimit
	for _, o := range objects {
		n := o.(*corev1.Namespace)
		// skip itself
		if n.Name == ns.Name {
			continue
		}
		nsLimit, err := getNamespaceLimit(ns)
		if err != nil {
			return false, err
		}
		nsLimits = append(nsLimits, nsLimit)
	}
	nsLimit, err := getNamespaceLimit(&finalNs)
	if err != nil {
		return false, err
	}
	isFit, msg, err := validate.IsQuotaFit(nsLimit, nsLimits, projectLimit)
	if err != nil {
		return false, err
	}
	return isFit, c.setValidated(&finalNs, isFit, msg)
}

func (c *SyncController) setValidated(ns *corev1.Namespace, value bool, msg string) error {
	set, err := namespaceutil.IsNamespaceConditionSet(ns, resourceQuotaValidatedCondition, value)
	if set || err != nil {
		return err
	}

	toUpdate := ns.DeepCopy()
	err = namespaceutil.SetNamespaceCondition(toUpdate, time.Second*1, resourceQuotaValidatedCondition, value, msg)
	if err != nil {
		return err
	}
	_, err = c.Namespaces.Update(toUpdate)

	return err
}

func getProjectLock(projectID string) *sync.Mutex {
	val, ok := projectLockCache.Get(projectID)
	if !ok {
		projectLockCache.Add(projectID, &sync.Mutex{}, time.Hour)
		val, _ = projectLockCache.Get(projectID)
	}
	mu := val.(*sync.Mutex)
	return mu
}

func (c *SyncController) getResourceQuotaToUpdate(ns *corev1.Namespace) (string, error) {
	if getNamespaceResourceQuota(ns) != "" {
		return "", nil
	}

	quota, err := getProjectNamespaceDefaultQuota(ns, c.ProjectLister)
	if err != nil {
		return "", err
	}
	b, err := json.Marshal(quota)
	if err != nil {
		return "", err
	}
	return string(b), nil

}
<commit_msg>Reset resource quota when ns is moved to project with no quota set<commit_after>package resourcequota

import (
	"encoding/json"
	"reflect"
	"sync"
	"time"

	namespaceutil "github.com/rancher/rancher/pkg/namespace"
	validate "github.com/rancher/rancher/pkg/resourcequota"
	"github.com/rancher/types/apis/core/v1"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/cache"
	clientcache "k8s.io/client-go/tools/cache"
)

const (
	projectIDAnnotation             = "field.cattle.io/projectId"
	resourceQuotaLabel              = "resourcequota.management.cattle.io/default-resource-quota"
	resourceQuotaInitCondition      = "ResourceQuotaInit"
	resourceQuotaAnnotation         = "field.cattle.io/resourceQuota"
	resourceQuotaValidatedCondition = "ResourceQuotaValidated"
)

var (
	projectLockCache = cache.NewLRUExpireCache(1000)
)

/*
SyncController takes care of creating Kubernetes resource quota based on the resource limits
defined in namespace.resourceQuota
*/
type SyncController struct {
	ProjectLister       v3.ProjectLister
	Namespaces          v1.NamespaceInterface
	NamespaceLister     v1.NamespaceLister
	ResourceQuotas      v1.ResourceQuotaInterface
	ResourceQuotaLister v1.ResourceQuotaLister
	NsIndexer           clientcache.Indexer
}

func (c *SyncController) syncResourceQuota(key string, ns *corev1.Namespace) error {
	if ns == nil || ns.DeletionTimestamp != nil {
		return nil
	}

	_, err := c.CreateResourceQuota(ns)
	return err
}

func (c *SyncController) CreateResourceQuota(ns *corev1.Namespace) (*corev1.Namespace, error) {
	existing, err := c.getExistingResourceQuota(ns)
	if err != nil {
		return ns, err
	}

	projectLimit, _, err := getProjectResourceQuotaLimit(ns, c.ProjectLister)
	if err != nil {
		return ns, err
	}

	var quotaSpec *corev1.ResourceQuotaSpec
	if projectLimit != nil {
		quotaSpec, err = c.getNamespaceResourceQuota(ns, true)
		if err != nil {
			return ns, err
		}
	}

	operation := "none"
	if existing == nil {
		if quotaSpec != nil {
			operation = "create"
		}
	} else {
		if quotaSpec == nil {
			operation = "delete"
		} else if !reflect.DeepEqual(existing.Spec.Hard, quotaSpec.Hard) {
			operation = "update"
		}
	}

	switch operation {
	case "create":
		isFit, err := c.validateNamespaceQuota(ns)
		if err != nil || !isFit {
			return ns, err
		}
		err = c.createDefaultResourceQuota(ns, quotaSpec)
	case "update":
		isFit, err := c.validateNamespaceQuota(ns)
		if err != nil || !isFit {
			return ns, err
		}
		err = c.updateResourceQuota(existing, quotaSpec)
	case "delete":
		err = c.deleteResourceQuota(existing)
	}
	if err == nil {
		set, err := namespaceutil.IsNamespaceConditionSet(ns, resourceQuotaInitCondition, true)
		if err != nil || set {
			return ns, err
		}
		toUpdate, err := c.Namespaces.Get(ns.Name, metav1.GetOptions{})
		if err != nil {
			return toUpdate, err
		}
		namespaceutil.SetNamespaceCondition(toUpdate, time.Second*1, resourceQuotaInitCondition, true, "")
		return c.Namespaces.Update(toUpdate)
	}

	return ns, err
}

func (c *SyncController) updateResourceQuota(quota *corev1.ResourceQuota, spec *corev1.ResourceQuotaSpec) error {
	toUpdate := quota.DeepCopy()
	toUpdate.Spec = *spec
	logrus.Infof("Updating default resource quota for namespace %v", toUpdate.Namespace)
	_, err := c.ResourceQuotas.Update(toUpdate)
	return err
}

func (c *SyncController) deleteResourceQuota(quota *corev1.ResourceQuota) error {
	logrus.Infof("Deleting default resource quota for namespace %v", quota.Namespace)
	return c.ResourceQuotas.DeleteNamespaced(quota.Namespace, quota.Name, &metav1.DeleteOptions{})
}

func (c *SyncController) getExistingResourceQuota(ns *corev1.Namespace) (*corev1.ResourceQuota, error) {
	set := labels.Set(map[string]string{resourceQuotaLabel: "true"})
	quota, err := c.ResourceQuotaLister.List(ns.Name, set.AsSelector())
	if err != nil {
		return nil, err
	}
	if len(quota) == 0 {
		return nil, nil
	}
	return quota[0], nil
}

func (c *SyncController) getNamespaceResourceQuota(ns *corev1.Namespace, setDefault bool) (*corev1.ResourceQuotaSpec, error) {
	limit, err := getNamespaceLimit(ns)
	if err != nil {
		return nil, err
	}
	if limit == nil {
		if setDefault {
			limit = defaultResourceLimit
		} else {
			return nil, nil
		}
	}

	return convertResourceLimitResourceQuotaSpec(limit)
}

var defaultResourceLimit = &v3.ResourceQuotaLimit{
	Pods:                   "0",
	Services:               "0",
	ReplicationControllers: "0",
	Secrets:                "0",
	ConfigMaps:             "0",
	PersistentVolumeClaims: "0",
	ServicesNodePorts:      "0",
	ServicesLoadBalancers:  "0",
	RequestsCPU:            "0",
	RequestsMemory:         "0",
	RequestsStorage:        "0",
	LimitsCPU:              "0",
	LimitsMemory:           "0",
}

func (c *SyncController) createDefaultResourceQuota(ns *corev1.Namespace, spec *corev1.ResourceQuotaSpec) error {
	resourceQuota := &corev1.ResourceQuota{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "default-",
			Namespace:    ns.Name,
			Labels:       map[string]string{resourceQuotaLabel: "true"},
		},
		Spec: *spec,
	}
	logrus.Infof("Creating default resource quota for namespace %v", ns.Name)
	_, err := c.ResourceQuotas.Create(resourceQuota)
	return err
}

func (c *SyncController) validateNamespaceQuota(ns *corev1.Namespace) (bool, error) {
	if ns == nil || ns.DeletionTimestamp != nil {
		return true, nil
	}

	// get project limit
	projectLimit, projectID, err := getProjectResourceQuotaLimit(ns, c.ProjectLister)
	if err != nil {
		return false, err
	}

	if projectLimit == nil {
		return true, err
	}

	// set default quota if not set
	quotaToUpdate, err := c.getResourceQuotaToUpdate(ns)
	if err != nil {
		return false, err
	}
	finalNs := *ns
	if quotaToUpdate != "" {
		toUpdate := ns.DeepCopy()
		if toUpdate.Annotations == nil {
			toUpdate.Annotations = map[string]string{}
		}
		toUpdate.Annotations[resourceQuotaAnnotation] = quotaToUpdate
		toUpdate, err = c.Namespaces.Update(toUpdate)
		if err != nil {
			return false, err
		}
		finalNs = *toUpdate
	}

	// validate resource quota
	mu := getProjectLock(projectID)
	mu.Lock()
	defer mu.Unlock()
	// get other Namespaces
	objects, err := c.NsIndexer.ByIndex(nsByProjectIndex, projectID)
	if err != nil {
		return false, err
	}
	var nsLimits []*v3.ResourceQuotaLimit
	for _, o := range objects {
		n := o.(*corev1.Namespace)
		// skip itself
		if n.Name == ns.Name {
			continue
		}
		nsLimit, err := getNamespaceLimit(ns)
		if err != nil {
			return false, err
		}
		nsLimits = append(nsLimits, nsLimit)
	}
	nsLimit, err := getNamespaceLimit(&finalNs)
	if err != nil {
		return false, err
	}
	isFit, msg, err := validate.IsQuotaFit(nsLimit, nsLimits, projectLimit)
	if err != nil {
		return false, err
	}
	return isFit, c.setValidated(&finalNs, isFit, msg)
}

func (c *SyncController) setValidated(ns *corev1.Namespace, value bool, msg string) error {
	set, err := namespaceutil.IsNamespaceConditionSet(ns, resourceQuotaValidatedCondition, value)
	if set || err != nil {
		return err
	}

	toUpdate := ns.DeepCopy()
	err = namespaceutil.SetNamespaceCondition(toUpdate, time.Second*1, resourceQuotaValidatedCondition, value, msg)
	if err != nil {
		return err
	}
	_, err = c.Namespaces.Update(toUpdate)

	return err
}

func getProjectLock(projectID string) *sync.Mutex {
	val, ok := projectLockCache.Get(projectID)
	if !ok {
		projectLockCache.Add(projectID, &sync.Mutex{}, time.Hour)
		val, _ = projectLockCache.Get(projectID)
	}
	mu := val.(*sync.Mutex)
	return mu
}

func (c *SyncController) getResourceQuotaToUpdate(ns *corev1.Namespace) (string, error) {
	if getNamespaceResourceQuota(ns) != "" {
		return "", nil
	}

	quota, err := getProjectNamespaceDefaultQuota(ns, c.ProjectLister)
	if err != nil {
		return "", err
	}
	b, err := json.Marshal(quota)
	if err != nil {
		return "", err
	}
	return string(b), nil

}
<|endoftext|>


<commit_before><commit_msg>crypto/rsa: left-pad PKCS#1 v1.5 outputs.<commit_after><|endoftext|>


<commit_before>package models

import (
	"github.com/jinzhu/gorm"
	"net/url"
	"time"
	"fmt"
)

type Image struct {
	gorm.Model
	Src    string    `json:"src"`
	Tags   []Tag     `gorm:"many2many:images_tags"`
	Date   time.Time `gorm:"not null" json:"date"`
	ChatID int64     `gorm:"not null" json:"-"`
	Chat   Chat      `gorm:"ForeignKey:ChatID;AssociationForeignKey:TGID"`
}

func (img *Image) GetImgByParams(db *gorm.DB, params url.Values) ([]Image, error) {
	img_slice := []Image{}
	q_tmp := db.Model(&Image{}).Preload("Tags").Preload("Chat")

	chat_id, ok := params["chat_id"]
	if ok {
		q_tmp = q_tmp.Where("chat_id = ?", chat_id[0])
	}
	date_from, ok := params["date_to"]
	if ok {
		q_tmp = q_tmp.Where("date > ?", date_from[0])
	}
	date_to, ok := params["date_to"]
	if ok {
		q_tmp = q_tmp.Where("date < ?", date_to[0])
	}

	if q_tmp.Find(&img_slice).RecordNotFound() {
		return nil, db.Error
	}

	if err := db.Error; err != nil {
		return nil, err
	}

	return img_slice, nil
}

func (img *Image) CreateImageWithTags(db *gorm.DB, ts []string) error {
	var tags []Tag
	for _, t := range ts {
		tags = append(tags, Tag{Name: t})
	}

	ch := Chat{
		TGID: img.ChatID,
	}

	tx := db.Begin()
	for _, t := range tags {
		t.Chats = append(t.Chats, ch)
		if err := t.SaveIfUnique(db); err != nil {
			tx.Rollback()
			return fmt.Errorf("unable to save tag: %s", err)
		}
	}
	if err := db.Create(img).Error; err != nil {
		tx.Rollback()
		return fmt.Errorf("unable to save image: %s", err)
	}
	if err := db.Model(&img).Association("Tags").Append(tags).Error;
		err != nil {
		tx.Rollback()
		return fmt.Errorf("unable to add img_tag association: %s", err)
	}

	return tx.Commit().Error
}
<commit_msg>image add reworked<commit_after>package models

import (
	"github.com/jinzhu/gorm"
	"net/url"
	"time"
	"fmt"
)

type Image struct {
	gorm.Model
	Src    string    `json:"src"`
	Tags   []Tag     `gorm:"many2many:images_tags"`
	Date   time.Time `gorm:"not null" json:"date"`
	ChatID int64     `gorm:"not null" json:"-"`
	Chat   Chat      `gorm:"ForeignKey:ChatID;AssociationForeignKey:TGID"`
}

func (img *Image) GetImgByParams(db *gorm.DB, params url.Values) ([]Image, error) {
	img_slice := []Image{}
	q_tmp := db.Model(&Image{}).Preload("Tags").Preload("Chat")

	chat_id, ok := params["chat_id"]
	if ok {
		q_tmp = q_tmp.Where("chat_id = ?", chat_id[0])
	}
	date_from, ok := params["date_to"]
	if ok {
		q_tmp = q_tmp.Where("date > ?", date_from[0])
	}
	date_to, ok := params["date_to"]
	if ok {
		q_tmp = q_tmp.Where("date < ?", date_to[0])
	}

	if q_tmp.Find(&img_slice).RecordNotFound() {
		return nil, db.Error
	}

	if err := db.Error; err != nil {
		return nil, err
	}

	return img_slice, nil
}

func (img *Image) CreateImageWithTags(db *gorm.DB, ts []string) error {
	var tags []Tag
	for _, t := range ts {
		tags = append(tags, Tag{Name: t})
	}

	ch := Chat{
		TGID: img.ChatID,
		Tags: tags,
	}

	tx := db.Begin()
	ch.Tags = tags
	if err := db.Model(&ch).Association("Tags").Append(tags).Error;
		err != nil{
		tx.Rollback()
		return fmt.Errorf("unable to add association chats_tags")
	}
	img.Tags = ch.Tags
	//for _, t := range tags {
	//	t.Chats = append(t.Chats, ch)
	//	if err := t.SaveIfUnique(db); err != nil {
	//		tx.Rollback()
	//		return fmt.Errorf("unable to save tag: %s", err)
	//	}
	//}
	if err := db.Create(img).Error; err != nil {
		tx.Rollback()
		return fmt.Errorf("unable to save image: %s", err)
	}
	//if err := db.Model(&img).Association("Tags").Append(tags).Error;
	//	err != nil {
	//	tx.Rollback()
	//	return fmt.Errorf("unable to add img_tag association: %s", err)
	//}

	return tx.Commit().Error
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"time"

	"github.com/Depado/bfchroma"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	bf "gopkg.in/russross/blackfriday.v2"
	"gopkg.in/yaml.v2"
)

var renderer = bfchroma.NewRenderer(
	bfchroma.WithoutAutodetect(),
	bfchroma.ChromaOptions(html.WithLineNumbers()),
	bfchroma.Extend(
		bf.NewHTMLRenderer(bf.HTMLRendererParameters{
			Flags: bf.CommonHTMLFlags | bf.TOC,
		}),
	),
)

func render(input []byte) []byte {
	return bf.Run(input, bf.WithRenderer(renderer), bf.WithExtensions(bf.Footnotes))
}

// MPages is the map containing all the articles. The key is the slug of the article
// for low complexity access when querying a slug.
var MPages map[string]*Page

// SPages is a sorted slice of pages. Sorted by date, it is used to render all
// the pages on the index page.
var SPages pageSlice

// Page is the main struct. It contains everything needed to render the article.
type Page struct {
	Raw         string
	Markdown    template.HTML
	Title       string
	Description string
	Author      string
	Date        time.Time
	DateFmt     string
	Tags        []string
	File        string
	Slug        string
}

// NewPageFromFile parses a file, inserts it in the map and slice, and returns a *Page instance
func NewPageFromFile(fn string) (*Page, error) {
	var err error
	p := new(Page)

	if err = p.ParseFile(fn); err != nil {
		return nil, errors.Wrap(err, "NewPageFromFile")
	}
	if err = p.Insert(false); err != nil {
		return nil, errors.Wrap(err, "NewPageFromFile")
	}

	logrus.WithFields(logrus.Fields{
		"file": filepath.Base(fn),
		"url":  fmt.Sprintf("/post/%s", p.Slug),
	}).Info("New file added")

	return p, nil
}

type pageSlice []*Page

// Len is part of sort.Interface.
func (p pageSlice) Len() int {
	return len(p)
}

// Swap is part of sort.Interface.
func (p pageSlice) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

// Less is part of sort.Interface. We use count as the value to sort by
func (p pageSlice) Less(i, j int) bool {
	return p[i].Date.After(p[j].Date)
}

// ParseFile parses a whole file and fills the Page struct.
func (p *Page) ParseFile(fn string) error {
	var err error

	p.File = filepath.Base(fn)
	h, b, err := SplitFile(fn)
	if err != nil {
		return err
	}
	p.Raw = string(b)
	p.ParseMarkdown(b)
	if err = p.ParseMetadata(h); err != nil {
		return err
	}
	return nil
}

// UpdateFromFile parses the file once more
func (p *Page) UpdateFromFile(fn string) error {
	var err error
	old := p.Slug
	if err = p.ParseFile(fn); err != nil {
		return errors.Wrap(err, "UpdateFromFile")
	}
	if _, ok := MPages[p.Slug]; !ok {
		delete(MPages, old)
		MPages[p.Slug] = p
	}
	sort.Sort(SPages)

	logrus.WithFields(logrus.Fields{
		"file": filepath.Base(fn),
		"url":  fmt.Sprintf("/post/%s", p.Slug),
	}).Info("Modified")

	return nil
}

// Pop removes a Page (in case the file is deleted for example)
func (p *Page) Pop() {
	for i, n := range SPages {
		if n == p {
			SPages = append(SPages[:i], SPages[i+1:]...)
		}
	}
	delete(MPages, p.Slug)
	logrus.WithFields(logrus.Fields{
		"file": filepath.Base(p.File),
		"url":  fmt.Sprintf("/post/%s", p.Slug),
	}).Info("Removed")
}

// ParseMetadata parses the metadata on top of the markdown files. It will also
// raise errors when mandatory fields aren't present, or some slugs are duplicates.
func (p *Page) ParseMetadata(h []byte) error {
	var err error
	var t time.Time

	m := meta{}
	if err = yaml.Unmarshal(h, &m); err != nil {
		return err
	}
	if err = m.Validate(); err != nil {
		return err
	}
	if t, err = time.Parse("2006-01-02 15:04:05", m.Date); err != nil {
		return err
	}
	p.Slug = m.GenerateSlug()
	p.Description = m.Description
	p.Date = t
	p.DateFmt = t.Format("2006/01/02 15:04")
	p.Tags = m.Tags
	p.Author = m.Author
	p.Title = m.Title
	return nil
}

// ParseMarkdown will simply parse the markdown b and put it inside the Page structure.
func (p *Page) ParseMarkdown(b []byte) {
	p.Markdown = template.HTML(string(render(b)))
}

// Insert will try to insert the file in the MPages map and SPages slice. It will
// also validates that no pages have the same slug, and sort the SPages slice in case
// it's not a batch insertion. (A batch insertion means after all the inserts,
// SPages will be sorted manually)
func (p *Page) Insert(batch bool) error {
	if val, ok := MPages[p.Slug]; ok {
		return fmt.Errorf("Two pages have the same slug : %s and %s both have %s", p.File, val.File, p.Slug)
	}
	MPages[p.Slug] = p
	SPages = append(SPages, p)
	if !batch {
		sort.Sort(SPages)
	}
	return nil
}

// ParseDir cycles through a directory and parses each file one by one.
func ParseDir(dir string) error {
	var err error
	var files []os.FileInfo
	start := time.Now()

	if files, err = ioutil.ReadDir(dir); err != nil {
		return err
	}

	MPages = make(map[string]*Page, len(files))
	SPages = make(pageSlice, 0, len(files))

	for _, f := range files {
		s := time.Now()
		p := Page{}
		if err = p.ParseFile(path.Join(dir, f.Name())); err != nil {
			logrus.WithFields(logrus.Fields{
				"took":  time.Since(s),
				"file":  p.File,
				"state": "ignored",
			}).WithError(err).Error("Couldn't process file")
			continue
		}
		if err = p.Insert(true); err != nil {
			logrus.WithFields(logrus.Fields{
				"took":  time.Since(s),
				"file":  p.File,
				"state": "ignored",
			}).WithError(err).Error("Couldn't insert file")
			continue
		}
		logrus.WithFields(logrus.Fields{
			"took": time.Since(s),
			"file": p.File,
			"url":  fmt.Sprintf("/post/%s", p.Slug),
		}).Info("Generated page successfully")
	}
	sort.Sort(SPages)
	logrus.WithFields(logrus.Fields{
		"files": len(files),
		"took":  time.Since(start),
	}).Info("Generated files")
	return nil
}
<commit_msg>Fixing extensions<commit_after>package models

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"time"

	"github.com/Depado/bfchroma"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	bf "gopkg.in/russross/blackfriday.v2"
	"gopkg.in/yaml.v2"
)

var renderer = bfchroma.NewRenderer(
	bfchroma.WithoutAutodetect(),
	bfchroma.ChromaOptions(html.WithLineNumbers()),
	bfchroma.Extend(
		bf.NewHTMLRenderer(bf.HTMLRendererParameters{
			Flags: bf.CommonHTMLFlags | bf.TOC,
		}),
	),
)

func render(input []byte) []byte {
	return bf.Run(input, bf.WithRenderer(renderer), bf.WithExtensions(bf.Footnotes|bf.FencedCode))
}

// MPages is the map containing all the articles. The key is the slug of the article
// for low complexity access when querying a slug.
var MPages map[string]*Page

// SPages is a sorted slice of pages. Sorted by date, it is used to render all
// the pages on the index page.
var SPages pageSlice

// Page is the main struct. It contains everything needed to render the article.
type Page struct {
	Raw         string
	Markdown    template.HTML
	Title       string
	Description string
	Author      string
	Date        time.Time
	DateFmt     string
	Tags        []string
	File        string
	Slug        string
}

// NewPageFromFile parses a file, inserts it in the map and slice, and returns a *Page instance
func NewPageFromFile(fn string) (*Page, error) {
	var err error
	p := new(Page)

	if err = p.ParseFile(fn); err != nil {
		return nil, errors.Wrap(err, "NewPageFromFile")
	}
	if err = p.Insert(false); err != nil {
		return nil, errors.Wrap(err, "NewPageFromFile")
	}

	logrus.WithFields(logrus.Fields{
		"file": filepath.Base(fn),
		"url":  fmt.Sprintf("/post/%s", p.Slug),
	}).Info("New file added")

	return p, nil
}

type pageSlice []*Page

// Len is part of sort.Interface.
func (p pageSlice) Len() int {
	return len(p)
}

// Swap is part of sort.Interface.
func (p pageSlice) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

// Less is part of sort.Interface. We use count as the value to sort by
func (p pageSlice) Less(i, j int) bool {
	return p[i].Date.After(p[j].Date)
}

// ParseFile parses a whole file and fills the Page struct.
func (p *Page) ParseFile(fn string) error {
	var err error

	p.File = filepath.Base(fn)
	h, b, err := SplitFile(fn)
	if err != nil {
		return err
	}
	p.Raw = string(b)
	p.ParseMarkdown(b)
	if err = p.ParseMetadata(h); err != nil {
		return err
	}
	return nil
}

// UpdateFromFile parses the file once more
func (p *Page) UpdateFromFile(fn string) error {
	var err error
	old := p.Slug
	if err = p.ParseFile(fn); err != nil {
		return errors.Wrap(err, "UpdateFromFile")
	}
	if _, ok := MPages[p.Slug]; !ok {
		delete(MPages, old)
		MPages[p.Slug] = p
	}
	sort.Sort(SPages)

	logrus.WithFields(logrus.Fields{
		"file": filepath.Base(fn),
		"url":  fmt.Sprintf("/post/%s", p.Slug),
	}).Info("Modified")

	return nil
}

// Pop removes a Page (in case the file is deleted for example)
func (p *Page) Pop() {
	for i, n := range SPages {
		if n == p {
			SPages = append(SPages[:i], SPages[i+1:]...)
		}
	}
	delete(MPages, p.Slug)
	logrus.WithFields(logrus.Fields{
		"file": filepath.Base(p.File),
		"url":  fmt.Sprintf("/post/%s", p.Slug),
	}).Info("Removed")
}

// ParseMetadata parses the metadata on top of the markdown files. It will also
// raise errors when mandatory fields aren't present, or some slugs are duplicates.
func (p *Page) ParseMetadata(h []byte) error {
	var err error
	var t time.Time

	m := meta{}
	if err = yaml.Unmarshal(h, &m); err != nil {
		return err
	}
	if err = m.Validate(); err != nil {
		return err
	}
	if t, err = time.Parse("2006-01-02 15:04:05", m.Date); err != nil {
		return err
	}
	p.Slug = m.GenerateSlug()
	p.Description = m.Description
	p.Date = t
	p.DateFmt = t.Format("2006/01/02 15:04")
	p.Tags = m.Tags
	p.Author = m.Author
	p.Title = m.Title
	return nil
}

// ParseMarkdown will simply parse the markdown b and put it inside the Page structure.
func (p *Page) ParseMarkdown(b []byte) {
	p.Markdown = template.HTML(string(render(b)))
}

// Insert will try to insert the file in the MPages map and SPages slice. It will
// also validates that no pages have the same slug, and sort the SPages slice in case
// it's not a batch insertion. (A batch insertion means after all the inserts,
// SPages will be sorted manually)
func (p *Page) Insert(batch bool) error {
	if val, ok := MPages[p.Slug]; ok {
		return fmt.Errorf("Two pages have the same slug : %s and %s both have %s", p.File, val.File, p.Slug)
	}
	MPages[p.Slug] = p
	SPages = append(SPages, p)
	if !batch {
		sort.Sort(SPages)
	}
	return nil
}

// ParseDir cycles through a directory and parses each file one by one.
func ParseDir(dir string) error {
	var err error
	var files []os.FileInfo
	start := time.Now()

	if files, err = ioutil.ReadDir(dir); err != nil {
		return err
	}

	MPages = make(map[string]*Page, len(files))
	SPages = make(pageSlice, 0, len(files))

	for _, f := range files {
		s := time.Now()
		p := Page{}
		if err = p.ParseFile(path.Join(dir, f.Name())); err != nil {
			logrus.WithFields(logrus.Fields{
				"took":  time.Since(s),
				"file":  p.File,
				"state": "ignored",
			}).WithError(err).Error("Couldn't process file")
			continue
		}
		if err = p.Insert(true); err != nil {
			logrus.WithFields(logrus.Fields{
				"took":  time.Since(s),
				"file":  p.File,
				"state": "ignored",
			}).WithError(err).Error("Couldn't insert file")
			continue
		}
		logrus.WithFields(logrus.Fields{
			"took": time.Since(s),
			"file": p.File,
			"url":  fmt.Sprintf("/post/%s", p.Slug),
		}).Info("Generated page successfully")
	}
	sort.Sort(SPages)
	logrus.WithFields(logrus.Fields{
		"files": len(files),
		"took":  time.Since(start),
	}).Info("Generated files")
	return nil
}
<|endoftext|>


<commit_before>package elastigo

type CatIndexInfo struct {
	Health   string
	Name     string
	Shards   int
	Replicas int
	Docs     CatIndexDocs
	Store    CatIndexStore
}

type CatIndexDocs struct {
	Count   int64
	Deleted int64
}

type CatIndexStore struct {
	Size    int64
	PriSize int64
}

type CatShardInfo struct {
	IndexName string
	Shard     int
	Primary   string
	State     string
	Docs      int64
	Store     int64
	NodeIP    string
	NodeName  string
}
<commit_msg>added status field to CatIndexInfo struct<commit_after>package elastigo

type CatIndexInfo struct {
	Health   string
	Status   string
	Name     string
	Shards   int
	Replicas int
	Docs     CatIndexDocs
	Store    CatIndexStore
}

type CatIndexDocs struct {
	Count   int64
	Deleted int64
}

type CatIndexStore struct {
	Size    int64
	PriSize int64
}

type CatShardInfo struct {
	IndexName string
	Shard     int
	Primary   string
	State     string
	Docs      int64
	Store     int64
	NodeIP    string
	NodeName  string
}
<|endoftext|>


<commit_before><commit_msg>Moved a few functions related to passwords and new users over to genericsite<commit_after><|endoftext|>


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

package hex

import (
	"bytes"
	"testing"
)

type encDecTest struct {
	enc string
	dec []byte
}

var encDecTests = []encDecTest{
	{"", []byte{}},
	{"0001020304050607", []byte{0, 1, 2, 3, 4, 5, 6, 7}},
	{"08090a0b0c0d0e0f", []byte{8, 9, 10, 11, 12, 13, 14, 15}},
	{"f0f1f2f3f4f5f6f7", []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7}},
	{"f8f9fafbfcfdfeff", []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}},
	{"67", []byte{'g'}},
	{"e3a1", []byte{0xe3, 0xa1}},
}

func TestEncode(t *testing.T) {
	for i, test := range encDecTests {
		dst := make([]byte, EncodedLen(len(test.dec)))
		n := Encode(dst, test.dec)
		if n != len(dst) {
			t.Errorf("#%d: bad return value: got: %d want: %d", i, n, len(dst))
		}
		if string(dst) != test.enc {
			t.Errorf("#%d: got: %#v want: %#v", i, dst, test.enc)
		}
	}
}

func TestDecode(t *testing.T) {
	// Case for decoding uppercase hex characters, since
	// Encode always uses lowercase.
	decTests := append(encDecTests, encDecTest{"F8F9FAFBFCFDFEFF", []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}})
	for i, test := range decTests {
		dst := make([]byte, DecodedLen(len(test.enc)))
		n, err := Decode(dst, []byte(test.enc))
		if err != nil {
			t.Errorf("#%d: bad return value: got:%d want:%d", i, n, len(dst))
		} else if !bytes.Equal(dst, test.dec) {
			t.Errorf("#%d: got: %#v want: %#v", i, dst, test.dec)
		}
	}
}

func TestEncodeToString(t *testing.T) {
	for i, test := range encDecTests {
		s := EncodeToString(test.dec)
		if s != test.enc {
			t.Errorf("#%d got:%s want:%s", i, s, test.enc)
		}
	}
}

func TestDecodeString(t *testing.T) {
	for i, test := range encDecTests {
		dst, err := DecodeString(test.enc)
		if err != nil {
			t.Errorf("#%d: unexpected err value: %s", i, err)
			continue
		}
		if !bytes.Equal(dst, test.dec) {
			t.Errorf("#%d: got: %#v want: #%v", i, dst, test.dec)
		}
	}
}

type errTest struct {
	in  string
	err string
}

var errTests = []errTest{
	{"0", "encoding/hex: odd length hex string"},
	{"0g", "encoding/hex: invalid byte: U+0067 'g'"},
	{"0\x01", "encoding/hex: invalid byte: U+0001"},
}

func TestInvalidErr(t *testing.T) {
	for i, test := range errTests {
		dst := make([]byte, DecodedLen(len(test.in)))
		_, err := Decode(dst, []byte(test.in))
		if err == nil {
			t.Errorf("#%d: expected error; got none", i)
		} else if err.Error() != test.err {
			t.Errorf("#%d: got: %v want: %v", i, err, test.err)
		}
	}
}

func TestInvalidStringErr(t *testing.T) {
	for i, test := range errTests {
		_, err := DecodeString(test.in)
		if err == nil {
			t.Errorf("#%d: expected error; got none", i)
		} else if err.Error() != test.err {
			t.Errorf("#%d: got: %v want: %v", i, err, test.err)
		}
	}
}

func TestDumper(t *testing.T) {
	var in [40]byte
	for i := range in {
		in[i] = byte(i + 30)
	}

	for stride := 1; stride < len(in); stride++ {
		var out bytes.Buffer
		dumper := Dumper(&out)
		done := 0
		for done < len(in) {
			todo := done + stride
			if todo > len(in) {
				todo = len(in)
			}
			dumper.Write(in[done:todo])
			done = todo
		}

		dumper.Close()
		if !bytes.Equal(out.Bytes(), expectedHexDump) {
			t.Errorf("stride: %d failed. got:\n%s\nwant:\n%s", stride, out.Bytes(), expectedHexDump)
		}
	}
}

func TestDump(t *testing.T) {
	var in [40]byte
	for i := range in {
		in[i] = byte(i + 30)
	}

	out := []byte(Dump(in[:]))
	if !bytes.Equal(out, expectedHexDump) {
		t.Errorf("got:\n%s\nwant:\n%s", out, expectedHexDump)
	}
}

var expectedHexDump = []byte(`00000000  1e 1f 20 21 22 23 24 25  26 27 28 29 2a 2b 2c 2d  |.. !"#$%&'()*+,-|
00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
00000020  3e 3f 40 41 42 43 44 45                           |>?@ABCDE|
`)
<commit_msg>encoding/hex: add Decode error test case<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hex

import (
	"bytes"
	"testing"
)

type encDecTest struct {
	enc string
	dec []byte
}

var encDecTests = []encDecTest{
	{"", []byte{}},
	{"0001020304050607", []byte{0, 1, 2, 3, 4, 5, 6, 7}},
	{"08090a0b0c0d0e0f", []byte{8, 9, 10, 11, 12, 13, 14, 15}},
	{"f0f1f2f3f4f5f6f7", []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7}},
	{"f8f9fafbfcfdfeff", []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}},
	{"67", []byte{'g'}},
	{"e3a1", []byte{0xe3, 0xa1}},
}

func TestEncode(t *testing.T) {
	for i, test := range encDecTests {
		dst := make([]byte, EncodedLen(len(test.dec)))
		n := Encode(dst, test.dec)
		if n != len(dst) {
			t.Errorf("#%d: bad return value: got: %d want: %d", i, n, len(dst))
		}
		if string(dst) != test.enc {
			t.Errorf("#%d: got: %#v want: %#v", i, dst, test.enc)
		}
	}
}

func TestDecode(t *testing.T) {
	// Case for decoding uppercase hex characters, since
	// Encode always uses lowercase.
	decTests := append(encDecTests, encDecTest{"F8F9FAFBFCFDFEFF", []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}})
	for i, test := range decTests {
		dst := make([]byte, DecodedLen(len(test.enc)))
		n, err := Decode(dst, []byte(test.enc))
		if err != nil {
			t.Errorf("#%d: bad return value: got:%d want:%d", i, n, len(dst))
		} else if !bytes.Equal(dst, test.dec) {
			t.Errorf("#%d: got: %#v want: %#v", i, dst, test.dec)
		}
	}
}

func TestEncodeToString(t *testing.T) {
	for i, test := range encDecTests {
		s := EncodeToString(test.dec)
		if s != test.enc {
			t.Errorf("#%d got:%s want:%s", i, s, test.enc)
		}
	}
}

func TestDecodeString(t *testing.T) {
	for i, test := range encDecTests {
		dst, err := DecodeString(test.enc)
		if err != nil {
			t.Errorf("#%d: unexpected err value: %s", i, err)
			continue
		}
		if !bytes.Equal(dst, test.dec) {
			t.Errorf("#%d: got: %#v want: #%v", i, dst, test.dec)
		}
	}
}

type errTest struct {
	in  string
	err string
}

var errTests = []errTest{
	{"0", "encoding/hex: odd length hex string"},
	{"0g", "encoding/hex: invalid byte: U+0067 'g'"},
	{"00gg", "encoding/hex: invalid byte: U+0067 'g'"},
	{"0\x01", "encoding/hex: invalid byte: U+0001"},
}

func TestInvalidErr(t *testing.T) {
	for i, test := range errTests {
		dst := make([]byte, DecodedLen(len(test.in)))
		_, err := Decode(dst, []byte(test.in))
		if err == nil {
			t.Errorf("#%d: expected error; got none", i)
		} else if err.Error() != test.err {
			t.Errorf("#%d: got: %v want: %v", i, err, test.err)
		}
	}
}

func TestInvalidStringErr(t *testing.T) {
	for i, test := range errTests {
		_, err := DecodeString(test.in)
		if err == nil {
			t.Errorf("#%d: expected error; got none", i)
		} else if err.Error() != test.err {
			t.Errorf("#%d: got: %v want: %v", i, err, test.err)
		}
	}
}

func TestDumper(t *testing.T) {
	var in [40]byte
	for i := range in {
		in[i] = byte(i + 30)
	}

	for stride := 1; stride < len(in); stride++ {
		var out bytes.Buffer
		dumper := Dumper(&out)
		done := 0
		for done < len(in) {
			todo := done + stride
			if todo > len(in) {
				todo = len(in)
			}
			dumper.Write(in[done:todo])
			done = todo
		}

		dumper.Close()
		if !bytes.Equal(out.Bytes(), expectedHexDump) {
			t.Errorf("stride: %d failed. got:\n%s\nwant:\n%s", stride, out.Bytes(), expectedHexDump)
		}
	}
}

func TestDump(t *testing.T) {
	var in [40]byte
	for i := range in {
		in[i] = byte(i + 30)
	}

	out := []byte(Dump(in[:]))
	if !bytes.Equal(out, expectedHexDump) {
		t.Errorf("got:\n%s\nwant:\n%s", out, expectedHexDump)
	}
}

var expectedHexDump = []byte(`00000000  1e 1f 20 21 22 23 24 25  26 27 28 29 2a 2b 2c 2d  |.. !"#$%&'()*+,-|
00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
00000020  3e 3f 40 41 42 43 44 45                           |>?@ABCDE|
`)
<|endoftext|>


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

// Package iotest implements Readers and Writers useful mainly for testing.
package iotest

import (
	"errors"
	"io"
)

// OneByteReader returns a Reader that implements
// each non-empty Read by reading one byte from r.
func OneByteReader(r io.Reader) io.Reader { return &oneByteReader{r} }

type oneByteReader struct {
	r io.Reader
}

func (r *oneByteReader) Read(p []byte) (int, error) {
	if len(p) == 0 {
		return 0, nil
	}
	return r.r.Read(p[0:1])
}

// HalfReader returns a Reader that implements Read
// by reading half as many requested bytes from r.
func HalfReader(r io.Reader) io.Reader { return &halfReader{r} }

type halfReader struct {
	r io.Reader
}

func (r *halfReader) Read(p []byte) (int, error) {
	return r.r.Read(p[0 : (len(p)+1)/2])
}

// DataErrReader returns a Reader that returns the final
// error with the last data read, instead of by itself with
// zero bytes of data.
func DataErrReader(r io.Reader) io.Reader { return &dataErrReader{r, nil, make([]byte, 1024)} }

type dataErrReader struct {
	r      io.Reader
	unread []byte
	data   []byte
}

func (r *dataErrReader) Read(p []byte) (n int, err error) {
	// loop because first call needs two reads:
	// one to get data and a second to look for an error.
	for {
		if len(r.unread) == 0 {
			n1, err1 := r.r.Read(r.data)
			r.unread = r.data[0:n1]
			err = err1
		}
		if n > 0 || err != nil {
			break
		}
		n = copy(p, r.unread)
		r.unread = r.unread[n:]
	}
	return
}

var ErrTimeout = errors.New("timeout")

// TimeoutReader returns ErrTimeout on the second read
// with no data.  Subsequent calls to read succeed.
func TimeoutReader(r io.Reader) io.Reader { return &timeoutReader{r, 0} }

type timeoutReader struct {
	r     io.Reader
	count int
}

func (r *timeoutReader) Read(p []byte) (int, error) {
	r.count++
	if r.count == 2 {
		return 0, ErrTimeout
	}
	return r.r.Read(p)
}
<commit_msg>testing/iotest: rephrase DataErrReader's description<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package iotest implements Readers and Writers useful mainly for testing.
package iotest

import (
	"errors"
	"io"
)

// OneByteReader returns a Reader that implements
// each non-empty Read by reading one byte from r.
func OneByteReader(r io.Reader) io.Reader { return &oneByteReader{r} }

type oneByteReader struct {
	r io.Reader
}

func (r *oneByteReader) Read(p []byte) (int, error) {
	if len(p) == 0 {
		return 0, nil
	}
	return r.r.Read(p[0:1])
}

// HalfReader returns a Reader that implements Read
// by reading half as many requested bytes from r.
func HalfReader(r io.Reader) io.Reader { return &halfReader{r} }

type halfReader struct {
	r io.Reader
}

func (r *halfReader) Read(p []byte) (int, error) {
	return r.r.Read(p[0 : (len(p)+1)/2])
}

// DataErrReader changes the way errors are handled by a Reader. Normally, a
// Reader returns an error (typically EOF) from the first Read call after the
// last piece of data is read. DataErrReader wraps a Reader and changes its
// behavior so the final error is returned along with the final data, instead
// of in the first call after the final data.
func DataErrReader(r io.Reader) io.Reader { return &dataErrReader{r, nil, make([]byte, 1024)} }

type dataErrReader struct {
	r      io.Reader
	unread []byte
	data   []byte
}

func (r *dataErrReader) Read(p []byte) (n int, err error) {
	// loop because first call needs two reads:
	// one to get data and a second to look for an error.
	for {
		if len(r.unread) == 0 {
			n1, err1 := r.r.Read(r.data)
			r.unread = r.data[0:n1]
			err = err1
		}
		if n > 0 || err != nil {
			break
		}
		n = copy(p, r.unread)
		r.unread = r.unread[n:]
	}
	return
}

var ErrTimeout = errors.New("timeout")

// TimeoutReader returns ErrTimeout on the second read
// with no data.  Subsequent calls to read succeed.
func TimeoutReader(r io.Reader) io.Reader { return &timeoutReader{r, 0} }

type timeoutReader struct {
	r     io.Reader
	count int
}

func (r *timeoutReader) Read(p []byte) (int, error) {
	r.count++
	if r.count == 2 {
		return 0, ErrTimeout
	}
	return r.r.Read(p)
}
<|endoftext|>


<commit_before>package gerrit

// AccessService contains Access Right related REST endpoints
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html
type AccessService struct {
	client *Client
}

// AccessSectionInfo describes the access rights that are assigned on a ref.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#access-section-info
type AccessSectionInfo struct {
	Permissions map[string]PermissionInfo `json:"permissions"`
}

// PermissionInfo entity contains information about an assigned permission.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#permission-info
type PermissionInfo struct {
	Label     string                        `json:"label,omitempty"`
	Exclusive bool                          `json:"exclusive"`
	Rules     map[string]PermissionRuleInfo `json:"rules"`
}

// PermissionRuleInfo entity contains information about a permission rule that is assigned to group.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#permission-rule-info
type PermissionRuleInfo struct {
	// TODO Possible values for action: ALLOW, DENY or BLOCK, INTERACTIVE and BATCH
	Action string `json:"action"`
	Force  bool   `json:"force"`
	Min    int    `json:"min"`
	Max    int    `json:"max"`
}

// ProjectAccessInfo entity contains information about the access rights for a project.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#project-access-info
type ProjectAccessInfo struct {
	Revision      string                       `json:"revision"`
	InheritsFrom  ProjectInfo                  `json:"inherits_from"`
	Local         map[string]AccessSectionInfo `json:"local"`
	IsOwner       bool                         `json:"is_owner"`
	OwnerOf       []string                     `json:"owner_of"`
	CanUpload     bool                         `json:"can_upload"`
	CanAdd        bool                         `json:"can_add"`
	ConfigVisible bool                         `json:"config_visible"`
}

// ListAccessRightsOptions specifies the parameters to the AccessService.ListAccessRights.
type ListAccessRightsOptions struct {
	// The projects for which the access rights should be returned must be specified as project options.
	// The project can be specified multiple times.
	Project []string `url:"project,omitempty"`
}

// ListAccessRights lists the access rights for projects.
// As result a map is returned that maps the project name to ProjectAccessInfo entities.
// The entries in the map are sorted by project name.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#list-access
func (s *AccessService) ListAccessRights(opt *ListAccessRightsOptions) (*map[string]ProjectAccessInfo, *Response, error) {
	u := "access/"

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	v := new(map[string]ProjectAccessInfo)
	resp, err := s.client.Call("GET", u, nil, v)
	return v, resp, err
}
<commit_msg>fix: update project-access-info fields<commit_after>package gerrit

// AccessService contains Access Right related REST endpoints
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html
type AccessService struct {
	client *Client
}

// AccessSectionInfo describes the access rights that are assigned on a ref.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#access-section-info
type AccessSectionInfo struct {
	Permissions map[string]PermissionInfo `json:"permissions"`
}

// PermissionInfo entity contains information about an assigned permission.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#permission-info
type PermissionInfo struct {
	Label     string                        `json:"label,omitempty"`
	Exclusive bool                          `json:"exclusive"`
	Rules     map[string]PermissionRuleInfo `json:"rules"`
}

// PermissionRuleInfo entity contains information about a permission rule that is assigned to group.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#permission-rule-info
type PermissionRuleInfo struct {
	// TODO Possible values for action: ALLOW, DENY or BLOCK, INTERACTIVE and BATCH
	Action string `json:"action"`
	Force  bool   `json:"force"`
	Min    int    `json:"min"`
	Max    int    `json:"max"`
}

// ProjectAccessInfo entity contains information about the access rights for a project.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#project-access-info
type ProjectAccessInfo struct {
	Revision       string                       `json:"revision"`
	InheritsFrom   ProjectInfo                  `json:"inherits_from"`
	Local          map[string]AccessSectionInfo `json:"local"`
	IsOwner        bool                         `json:"is_owner"`
	OwnerOf        []string                     `json:"owner_of"`
	CanUpload      bool                         `json:"can_upload"`
	CanAdd         bool                         `json:"can_add"`
	CanAddTags     bool                         `json:"can_add_tags"`
	ConfigVisible  bool                         `json:"config_visible"`
	Groups         map[string]GroupInfo         `json:"groups"`
	ConfigWebLinks []string                     `json:"configWebLinks"`
}

// ListAccessRightsOptions specifies the parameters to the AccessService.ListAccessRights.
type ListAccessRightsOptions struct {
	// The projects for which the access rights should be returned must be specified as project options.
	// The project can be specified multiple times.
	Project []string `url:"project,omitempty"`
}

// ListAccessRights lists the access rights for projects.
// As result a map is returned that maps the project name to ProjectAccessInfo entities.
// The entries in the map are sorted by project name.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-access.html#list-access
func (s *AccessService) ListAccessRights(opt *ListAccessRightsOptions) (*map[string]ProjectAccessInfo, *Response, error) {
	u := "access/"

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	v := new(map[string]ProjectAccessInfo)
	resp, err := s.client.Call("GET", u, nil, v)
	return v, resp, err
}
<|endoftext|>


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

package main

import (
	`fmt`
	`io/ioutil`
	`os`
	`path/filepath`
	`github.com/jscherff/cmdb/ci/peripheral/usb`
)

// audit performs a change audit against properties from the last checkin.
func audit(dev usb.Auditer) (err error) {

	var ch [][]string

	if dev.SN() == `` {
		sl.Printf(`device %s-%s skipping audit, no SN`, dev.VID(), dev.PID())
		return err
	}

	sl.Printf(`device %s-%s-%s fetching previous state from server`,
		dev.VID(), dev.PID(), dev.SN(),
	)

	var j []byte

	if j, err = checkout(dev); err != nil {
		j, err = dev.Clone().(usb.Auditer).JSON()
	}

	if err != nil {
		return err
	}

	if ch, err = dev.CompareJSON(j); err != nil {
		return err
	}

	sl.Printf(`device %s-%s-%s saving current state to server`,
		dev.VID(), dev.PID(), dev.SN(),
	)

	if err := checkin(dev); err != nil {
		el.Print(err) // err occluded later by sendAudit()
	}

	if len(ch) == 0 {
		sl.Printf(`device %s-%s-%s detected no changes`,
			dev.VID(), dev.PID(), dev.SN(),
		)
		return nil
	}

	sl.Printf(`device %s-%s-%s recording changes in change log`,
		dev.VID(), dev.PID(), dev.SN(),
	)

	for _, c := range ch {
		cl.Printf(`device %s-%s-%s modified: '%s' was '%s', now '%s'`,
			dev.VID(), dev.PID(), dev.SN(), c[0], c[1], c[2],
		)
	}

	sl.Printf(`device %s-%s-%s reporting changes to server`,
		dev.VID(), dev.PID(), dev.SN(),
	)

	dev.SetChanges(ch)

	return sendAudit(dev)
}

// report processes options and writes report to the selected destination.
func report(dev usb.Reporter) (err error) {

	var b []byte

	switch *fReportFormat {

	case `csv`:
		b, err = dev.CSV()

	case `nvp`:
		b, err = dev.NVP()

	case `xml`:
		b, err = dev.PrettyXML()

	case `json`:
		b, err = dev.PrettyJSON()

	default:
		err = fmt.Errorf(`invalid format '%s'`, *fReportFormat)
	}

	if err != nil {
		return err
	}

	if *fReportConsole {
		fmt.Fprintln(os.Stdout, string(b))
		return nil
	}

	f := fmt.Sprintf(`%s-V%s-P%s.%s`, dev.Conn(), dev.VID(), dev.PID(), *fReportFormat)

	if *fReportFolder != `` {
		f = filepath.Join(*fReportFolder, f)
	} else {
		f = filepath.Join(conf.Paths.ReportDir, f)
	}

	return ioutil.WriteFile(f, b, FileMode)
}

// serial processes options and configures the the serial number.
func serial(dev usb.Serializer) (err error) {

	var s string

	if *fSerialErase {

		sl.Printf(`device %s-%s erasing serial '%s'`,
			dev.VID(), dev.PID(), dev.SN(),
		)

		if err = dev.EraseDeviceSN(); err != nil {
			return err
		}
	}

	if !*fSerialForce && dev.SN() != `` {

		return fmt.Errorf(`device %s-%s serial already set to '%s'`,
			dev.VID(), dev.PID(), dev.SN(),
		)

	}

	switch {

	case *fSerialFetch:

		if s, err = newSn(dev); err != nil {
			break
		}

		sl.Printf(`device %s-%s setting serial to '%s'`,
			dev.VID(), dev.PID(), s,
		)

		if err = dev.SetDeviceSN(s); err != nil {
			break
		}

		sl.Printf(`device %s-%s-%s checking in with server`,
			dev.VID(), dev.PID(), dev.SN(),
		)

		err = checkin(dev)

	case *fSerialDefault:

		sl.Printf(`device %s-%s setting serial to default`,
			dev.VID(), dev.PID(),
		)

		err = dev.SetDefaultSN()

	case *fSerialSet != ``:

		sl.Printf(`device %s-%s setting serial to '%s'`,
			dev.VID(), dev.PID(), *fSerialSet,
		)

		err = dev.SetDeviceSN(*fSerialSet)
	}

	return err
}

// showState displays any available diagnostic information about the device.
func showState(dev usb.Analyzer) (error) {

	if state, err := dev.GetState(); err != nil {
		return err
	} else {
		fmt.Println(state)
	}

	return nil
}
<commit_msg>Removed unnecessary logging of all properties as changes for new device<commit_after>// Copyright 2017 John Scherff
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	 http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	`fmt`
	`io/ioutil`
	`os`
	`path/filepath`
	`github.com/jscherff/cmdb/ci/peripheral/usb`
)

// audit performs a change audit against properties from the last checkin.
func audit(dev usb.Auditer) (err error) {

	var ch [][]string

	if dev.SN() == `` {
		sl.Printf(`device %s-%s skipping audit, no serial number`,
			dev.VID(), dev.PID(),
		)
		return err
	}

	sl.Printf(`device %s-%s-%s fetching previous state from server`,
		dev.VID(), dev.PID(), dev.SN(),
	)

	var j []byte

	if j, err = checkout(dev); err != nil {
		sl.Printf(`device %s-%s-%s skipping audit: no previous state`,
			dev.VID(), dev.PID(), dev.SN(),
		)
		return err
	}

	if ch, err = dev.CompareJSON(j); err != nil {
		return err
	}

	sl.Printf(`device %s-%s-%s saving current state to server`,
		dev.VID(), dev.PID(), dev.SN(),
	)

	if err := checkin(dev); err != nil {
		el.Print(err) // err occluded later by sendAudit()
	}

	if len(ch) == 0 {
		sl.Printf(`device %s-%s-%s detected no changes`,
			dev.VID(), dev.PID(), dev.SN(),
		)
		return nil
	}

	sl.Printf(`device %s-%s-%s recording changes in change log`,
		dev.VID(), dev.PID(), dev.SN(),
	)

	for _, c := range ch {
		cl.Printf(`device %s-%s-%s modified: '%s' was '%s', now '%s'`,
			dev.VID(), dev.PID(), dev.SN(), c[0], c[1], c[2],
		)
	}

	sl.Printf(`device %s-%s-%s reporting changes to server`,
		dev.VID(), dev.PID(), dev.SN(),
	)

	dev.SetChanges(ch)

	return sendAudit(dev)
}

// report processes options and writes report to the selected destination.
func report(dev usb.Reporter) (err error) {

	var b []byte

	switch *fReportFormat {

	case `csv`:
		b, err = dev.CSV()

	case `nvp`:
		b, err = dev.NVP()

	case `xml`:
		b, err = dev.PrettyXML()

	case `json`:
		b, err = dev.PrettyJSON()

	default:
		err = fmt.Errorf(`invalid format '%s'`, *fReportFormat)
	}

	if err != nil {
		return err
	}

	if *fReportConsole {
		fmt.Fprintln(os.Stdout, string(b))
		return nil
	}

	f := fmt.Sprintf(`%s-V%s-P%s.%s`, dev.Conn(), dev.VID(), dev.PID(), *fReportFormat)

	if *fReportFolder != `` {
		f = filepath.Join(*fReportFolder, f)
	} else {
		f = filepath.Join(conf.Paths.ReportDir, f)
	}

	return ioutil.WriteFile(f, b, FileMode)
}

// serial processes options and configures the the serial number.
func serial(dev usb.Serializer) (err error) {

	var s string

	if *fSerialErase {

		sl.Printf(`device %s-%s erasing serial '%s'`,
			dev.VID(), dev.PID(), dev.SN(),
		)

		if err = dev.EraseDeviceSN(); err != nil {
			return err
		}
	}

	if !*fSerialForce && dev.SN() != `` {

		return fmt.Errorf(`device %s-%s serial already set to '%s'`,
			dev.VID(), dev.PID(), dev.SN(),
		)

	}

	switch {

	case *fSerialFetch:

		if s, err = newSn(dev); err != nil {
			break
		}

		sl.Printf(`device %s-%s setting serial to '%s'`,
			dev.VID(), dev.PID(), s,
		)

		if err = dev.SetDeviceSN(s); err != nil {
			break
		}

		sl.Printf(`device %s-%s-%s checking in with server`,
			dev.VID(), dev.PID(), dev.SN(),
		)

		err = checkin(dev)

	case *fSerialDefault:

		sl.Printf(`device %s-%s setting serial to default`,
			dev.VID(), dev.PID(),
		)

		err = dev.SetDefaultSN()

	case *fSerialSet != ``:

		sl.Printf(`device %s-%s setting serial to '%s'`,
			dev.VID(), dev.PID(), *fSerialSet,
		)

		err = dev.SetDeviceSN(*fSerialSet)
	}

	return err
}

// showState displays any available diagnostic information about the device.
func showState(dev usb.Analyzer) (error) {

	if state, err := dev.GetState(); err != nil {
		return err
	} else {
		fmt.Println(state)
	}

	return nil
}
<|endoftext|>