<commit_before>//go:build linux
// +build linux

/*
Copyright 2021 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 e2enode

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"k8s.io/apimachinery/pkg/fields"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	"k8s.io/kubernetes/pkg/apis/scheduling"
	"k8s.io/kubernetes/pkg/features"
	"k8s.io/kubernetes/test/e2e/framework"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
	kubelettypes "k8s.io/kubernetes/pkg/kubelet/types"
	testutils "k8s.io/kubernetes/test/utils"
)

var _ = SIGDescribe("GracefulNodeShutdown [Serial] [NodeAlphaFeature:GracefulNodeShutdown]", func() {
	f := framework.NewDefaultFramework("graceful-node-shutdown")
	ginkgo.Context("when gracefully shutting down", func() {

		const (
			pollInterval                        = 1 * time.Second
			podStatusUpdateTimeout              = 5 * time.Second
			nodeStatusUpdateTimeout             = 10 * time.Second
			nodeShutdownGracePeriod             = 20 * time.Second
			nodeShutdownGracePeriodCriticalPods = 10 * time.Second
		)

		tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) {
			initialConfig.FeatureGates = map[string]bool{
				string(features.GracefulNodeShutdown): true,
			}
			initialConfig.ShutdownGracePeriod = metav1.Duration{Duration: nodeShutdownGracePeriod}
			initialConfig.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: nodeShutdownGracePeriodCriticalPods}
		})

		ginkgo.BeforeEach(func() {
			ginkgo.By("Wait for the node to be ready")
			waitForNodeReady()
		})

		ginkgo.AfterEach(func() {
			ginkgo.By("Emitting Shutdown false signal; cancelling the shutdown")
			err := emitSignalPrepareForShutdown(false)
			framework.ExpectNoError(err)
		})

		ginkgo.It("should be able to gracefully shutdown pods with various grace periods", func() {
			nodeName := getNodeName(f)
			nodeSelector := fields.Set{
				"spec.nodeName": nodeName,
			}.AsSelector().String()

			// Define test pods
			pods := []*v1.Pod{
				getGracePeriodOverrideTestPod("period-120", nodeName, 120, false),
				getGracePeriodOverrideTestPod("period-5", nodeName, 5, false),
				getGracePeriodOverrideTestPod("period-critical-120", nodeName, 120, true),
				getGracePeriodOverrideTestPod("period-critical-5", nodeName, 5, true),
			}

			ginkgo.By("Creating batch pods")
			f.PodClient().CreateBatch(pods)

			list, err := f.PodClient().List(context.TODO(), metav1.ListOptions{
				FieldSelector: nodeSelector,
			})
			framework.ExpectNoError(err)
			framework.ExpectEqual(len(list.Items), len(pods), "the number of pods is not as expected")

			ginkgo.By("Verifying batch pods are running")
			for _, pod := range list.Items {
				if podReady, err := testutils.PodRunningReady(&pod); err != nil || !podReady {
					framework.Failf("Failed to start batch pod: %v", pod.Name)
				}
			}

			ginkgo.By("Emitting shutdown signal")
			err = emitSignalPrepareForShutdown(true)
			framework.ExpectNoError(err)

			ginkgo.By("Verifying that non-critical pods are shutdown")
			// Not critical pod should be shutdown
			gomega.Eventually(func() error {
				list, err = f.PodClient().List(context.TODO(), metav1.ListOptions{
					FieldSelector: nodeSelector,
				})
				if err != nil {
					return err
				}
				framework.ExpectEqual(len(list.Items), len(pods), "the number of pods is not as expected")

				for _, pod := range list.Items {
					if kubelettypes.IsCriticalPod(&pod) {
						if pod.Status.Phase != v1.PodRunning {
							framework.Logf("Expecting critcal pod to be running, but it's not currently. Pod: %q, Pod Status Phase: %q, Pod Status Reason: %q", pod.Name, pod.Status.Phase, pod.Status.Reason)
							return fmt.Errorf("critical pod should not be shutdown, phase: %s", pod.Status.Phase)
						}
					} else {
						if pod.Status.Phase != v1.PodFailed || pod.Status.Reason != "Shutdown" {
							framework.Logf("Expecting non-critcal pod to be shutdown, but it's not currently. Pod: %q, Pod Status Phase: %q, Pod Status Reason: %q", pod.Name, pod.Status.Phase, pod.Status.Reason)
							return fmt.Errorf("pod should be shutdown, phase: %s", pod.Status.Phase)
						}
					}
				}
				return nil
			}, podStatusUpdateTimeout, pollInterval).Should(gomega.BeNil())

			ginkgo.By("Verifying that all pods are shutdown")
			// All pod should be shutdown
			gomega.Eventually(func() error {
				list, err = f.PodClient().List(context.TODO(), metav1.ListOptions{
					FieldSelector: nodeSelector,
				})
				if err != nil {
					return err
				}
				framework.ExpectEqual(len(list.Items), len(pods), "the number of pods is not as expected")

				for _, pod := range list.Items {
					if pod.Status.Phase != v1.PodFailed || pod.Status.Reason != "Shutdown" {
						framework.Logf("Expecting pod to be shutdown, but it's not currently: Pod: %q, Pod Status Phase: %q, Pod Status Reason: %q", pod.Name, pod.Status.Phase, pod.Status.Reason)
						return fmt.Errorf("pod should be shutdown, phase: %s", pod.Status.Phase)
					}
				}
				return nil
			},
				// Critical pod starts shutdown after (nodeShutdownGracePeriod-nodeShutdownGracePeriodCriticalPods)
				podStatusUpdateTimeout+(nodeShutdownGracePeriod-nodeShutdownGracePeriodCriticalPods),
				pollInterval).Should(gomega.BeNil())
		})

		ginkgo.It("should be able to handle a cancelled shutdown", func() {
			ginkgo.By("Emitting Shutdown signal")
			err := emitSignalPrepareForShutdown(true)
			framework.ExpectNoError(err)
			gomega.Eventually(func() error {
				isReady := getNodeReadyStatus(f)
				if isReady {
					return fmt.Errorf("node did not become shutdown as expected")
				}
				return nil
			}, nodeStatusUpdateTimeout, pollInterval).Should(gomega.BeNil())

			ginkgo.By("Emitting Shutdown false signal; cancelling the shutdown")
			err = emitSignalPrepareForShutdown(false)
			framework.ExpectNoError(err)
			gomega.Eventually(func() error {
				isReady := getNodeReadyStatus(f)
				if !isReady {
					return fmt.Errorf("node did not recover as expected")
				}
				return nil
			}, nodeStatusUpdateTimeout, pollInterval).Should(gomega.BeNil())
		})

		ginkgo.It("after restart dbus, should be able to gracefully shutdown", func() {
			// allows manual restart of dbus to work in Ubuntu.
			err := overlayDbusConfig()
			framework.ExpectNoError(err)
			defer func() {
				err := restoreDbusConfig()
				framework.ExpectNoError(err)
			}()

			ginkgo.By("Restart Dbus")
			err = restartDbus()
			framework.ExpectNoError(err)

			ginkgo.By("Emitting Shutdown signal")
			err = emitSignalPrepareForShutdown(true)
			framework.ExpectNoError(err)

			gomega.Eventually(func() error {
				isReady := getNodeReadyStatus(f)
				if isReady {
					return fmt.Errorf("node did not become shutdown as expected")
				}
				return nil
			}, nodeStatusUpdateTimeout, pollInterval).Should(gomega.BeNil())
		})
	})
})

func getGracePeriodOverrideTestPod(name string, node string, gracePeriod int64, critical bool) *v1.Pod {
	pod := &v1.Pod{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Pod",
			APIVersion: "v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:    name,
					Image:   busyboxImage,
					Command: []string{"sh", "-c"},
					Args: []string{`
_term() {
	echo "Caught SIGTERM signal!"
	while true; do sleep 5; done
}
trap _term SIGTERM
while true; do sleep 5; done
`},
				},
			},
			TerminationGracePeriodSeconds: &gracePeriod,
			NodeName:                      node,
		},
	}
	if critical {
		pod.ObjectMeta.Annotations = map[string]string{
			kubelettypes.ConfigSourceAnnotationKey: kubelettypes.FileSource,
		}
		pod.Spec.PriorityClassName = scheduling.SystemNodeCritical

		framework.ExpectEqual(kubelettypes.IsCriticalPod(pod), true, "pod should be a critical pod")
	} else {
		framework.ExpectEqual(kubelettypes.IsCriticalPod(pod), false, "pod should not be a critical pod")
	}
	return pod
}

// Emits a fake PrepareForShutdown dbus message on system dbus. Will cause kubelet to react to an active shutdown event.
func emitSignalPrepareForShutdown(b bool) error {
	cmd := "dbus-send --system /org/freedesktop/login1 org.freedesktop.login1.Manager.PrepareForShutdown boolean:" + strconv.FormatBool(b)
	_, err := runCommand("sh", "-c", cmd)
	return err
}

func getNodeReadyStatus(f *framework.Framework) bool {
	nodeList, err := f.ClientSet.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
	framework.ExpectNoError(err)
	// Assuming that there is only one node, because this is a node e2e test.
	framework.ExpectEqual(len(nodeList.Items), 1)
	return isNodeReady(&nodeList.Items[0])
}

func restartDbus() error {
	cmd := "systemctl restart dbus"
	_, err := runCommand("sh", "-c", cmd)
	return err
}

func systemctlDaemonReload() error {
	cmd := "systemctl daemon-reload"
	_, err := runCommand("sh", "-c", cmd)
	return err
}

var (
	dbusConfPath = "/etc/systemd/system/dbus.service.d/k8s-graceful-node-shutdown-e2e.conf"
	dbusConf     = `
[Unit]
RefuseManualStart=no
RefuseManualStop=no
[Service]
KillMode=control-group
ExecStop=
`
)

func overlayDbusConfig() error {
	err := os.MkdirAll(filepath.Dir(dbusConfPath), 0755)
	if err != nil {
		return err
	}
	err = os.WriteFile(dbusConfPath, []byte(dbusConf), 0644)
	if err != nil {
		return err
	}
	return systemctlDaemonReload()
}

func restoreDbusConfig() error {
	err := os.Remove(dbusConfPath)
	if err != nil {
		return err
	}
	return systemctlDaemonReload()
}
<commit_msg>e2e_node: NodeGracefulShutdown is a Beta feature<commit_after>//go:build linux
// +build linux

/*
Copyright 2021 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 e2enode

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"k8s.io/apimachinery/pkg/fields"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	"k8s.io/kubernetes/pkg/apis/scheduling"
	"k8s.io/kubernetes/test/e2e/framework"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
	kubelettypes "k8s.io/kubernetes/pkg/kubelet/types"
	testutils "k8s.io/kubernetes/test/utils"
)

var _ = SIGDescribe("GracefulNodeShutdown [Serial] [NodeFeature:GracefulNodeShutdown]", func() {
	f := framework.NewDefaultFramework("graceful-node-shutdown")
	ginkgo.Context("when gracefully shutting down", func() {

		const (
			pollInterval                        = 1 * time.Second
			podStatusUpdateTimeout              = 5 * time.Second
			nodeStatusUpdateTimeout             = 10 * time.Second
			nodeShutdownGracePeriod             = 20 * time.Second
			nodeShutdownGracePeriodCriticalPods = 10 * time.Second
		)

		tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) {
			initialConfig.ShutdownGracePeriod = metav1.Duration{Duration: nodeShutdownGracePeriod}
			initialConfig.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: nodeShutdownGracePeriodCriticalPods}
		})

		ginkgo.BeforeEach(func() {
			ginkgo.By("Wait for the node to be ready")
			waitForNodeReady()
		})

		ginkgo.AfterEach(func() {
			ginkgo.By("Emitting Shutdown false signal; cancelling the shutdown")
			err := emitSignalPrepareForShutdown(false)
			framework.ExpectNoError(err)
		})

		ginkgo.It("should be able to gracefully shutdown pods with various grace periods", func() {
			nodeName := getNodeName(f)
			nodeSelector := fields.Set{
				"spec.nodeName": nodeName,
			}.AsSelector().String()

			// Define test pods
			pods := []*v1.Pod{
				getGracePeriodOverrideTestPod("period-120", nodeName, 120, false),
				getGracePeriodOverrideTestPod("period-5", nodeName, 5, false),
				getGracePeriodOverrideTestPod("period-critical-120", nodeName, 120, true),
				getGracePeriodOverrideTestPod("period-critical-5", nodeName, 5, true),
			}

			ginkgo.By("Creating batch pods")
			f.PodClient().CreateBatch(pods)

			list, err := f.PodClient().List(context.TODO(), metav1.ListOptions{
				FieldSelector: nodeSelector,
			})
			framework.ExpectNoError(err)
			framework.ExpectEqual(len(list.Items), len(pods), "the number of pods is not as expected")

			ginkgo.By("Verifying batch pods are running")
			for _, pod := range list.Items {
				if podReady, err := testutils.PodRunningReady(&pod); err != nil || !podReady {
					framework.Failf("Failed to start batch pod: %v", pod.Name)
				}
			}

			ginkgo.By("Emitting shutdown signal")
			err = emitSignalPrepareForShutdown(true)
			framework.ExpectNoError(err)

			ginkgo.By("Verifying that non-critical pods are shutdown")
			// Not critical pod should be shutdown
			gomega.Eventually(func() error {
				list, err = f.PodClient().List(context.TODO(), metav1.ListOptions{
					FieldSelector: nodeSelector,
				})
				if err != nil {
					return err
				}
				framework.ExpectEqual(len(list.Items), len(pods), "the number of pods is not as expected")

				for _, pod := range list.Items {
					if kubelettypes.IsCriticalPod(&pod) {
						if pod.Status.Phase != v1.PodRunning {
							framework.Logf("Expecting critcal pod to be running, but it's not currently. Pod: %q, Pod Status Phase: %q, Pod Status Reason: %q", pod.Name, pod.Status.Phase, pod.Status.Reason)
							return fmt.Errorf("critical pod should not be shutdown, phase: %s", pod.Status.Phase)
						}
					} else {
						if pod.Status.Phase != v1.PodFailed || pod.Status.Reason != "Shutdown" {
							framework.Logf("Expecting non-critcal pod to be shutdown, but it's not currently. Pod: %q, Pod Status Phase: %q, Pod Status Reason: %q", pod.Name, pod.Status.Phase, pod.Status.Reason)
							return fmt.Errorf("pod should be shutdown, phase: %s", pod.Status.Phase)
						}
					}
				}
				return nil
			}, podStatusUpdateTimeout, pollInterval).Should(gomega.BeNil())

			ginkgo.By("Verifying that all pods are shutdown")
			// All pod should be shutdown
			gomega.Eventually(func() error {
				list, err = f.PodClient().List(context.TODO(), metav1.ListOptions{
					FieldSelector: nodeSelector,
				})
				if err != nil {
					return err
				}
				framework.ExpectEqual(len(list.Items), len(pods), "the number of pods is not as expected")

				for _, pod := range list.Items {
					if pod.Status.Phase != v1.PodFailed || pod.Status.Reason != "Shutdown" {
						framework.Logf("Expecting pod to be shutdown, but it's not currently: Pod: %q, Pod Status Phase: %q, Pod Status Reason: %q", pod.Name, pod.Status.Phase, pod.Status.Reason)
						return fmt.Errorf("pod should be shutdown, phase: %s", pod.Status.Phase)
					}
				}
				return nil
			},
				// Critical pod starts shutdown after (nodeShutdownGracePeriod-nodeShutdownGracePeriodCriticalPods)
				podStatusUpdateTimeout+(nodeShutdownGracePeriod-nodeShutdownGracePeriodCriticalPods),
				pollInterval).Should(gomega.BeNil())
		})

		ginkgo.It("should be able to handle a cancelled shutdown", func() {
			ginkgo.By("Emitting Shutdown signal")
			err := emitSignalPrepareForShutdown(true)
			framework.ExpectNoError(err)
			gomega.Eventually(func() error {
				isReady := getNodeReadyStatus(f)
				if isReady {
					return fmt.Errorf("node did not become shutdown as expected")
				}
				return nil
			}, nodeStatusUpdateTimeout, pollInterval).Should(gomega.BeNil())

			ginkgo.By("Emitting Shutdown false signal; cancelling the shutdown")
			err = emitSignalPrepareForShutdown(false)
			framework.ExpectNoError(err)
			gomega.Eventually(func() error {
				isReady := getNodeReadyStatus(f)
				if !isReady {
					return fmt.Errorf("node did not recover as expected")
				}
				return nil
			}, nodeStatusUpdateTimeout, pollInterval).Should(gomega.BeNil())
		})

		ginkgo.It("after restart dbus, should be able to gracefully shutdown", func() {
			// allows manual restart of dbus to work in Ubuntu.
			err := overlayDbusConfig()
			framework.ExpectNoError(err)
			defer func() {
				err := restoreDbusConfig()
				framework.ExpectNoError(err)
			}()

			ginkgo.By("Restart Dbus")
			err = restartDbus()
			framework.ExpectNoError(err)

			ginkgo.By("Emitting Shutdown signal")
			err = emitSignalPrepareForShutdown(true)
			framework.ExpectNoError(err)

			gomega.Eventually(func() error {
				isReady := getNodeReadyStatus(f)
				if isReady {
					return fmt.Errorf("node did not become shutdown as expected")
				}
				return nil
			}, nodeStatusUpdateTimeout, pollInterval).Should(gomega.BeNil())
		})
	})
})

func getGracePeriodOverrideTestPod(name string, node string, gracePeriod int64, critical bool) *v1.Pod {
	pod := &v1.Pod{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Pod",
			APIVersion: "v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:    name,
					Image:   busyboxImage,
					Command: []string{"sh", "-c"},
					Args: []string{`
_term() {
	echo "Caught SIGTERM signal!"
	while true; do sleep 5; done
}
trap _term SIGTERM
while true; do sleep 5; done
`},
				},
			},
			TerminationGracePeriodSeconds: &gracePeriod,
			NodeName:                      node,
		},
	}
	if critical {
		pod.ObjectMeta.Annotations = map[string]string{
			kubelettypes.ConfigSourceAnnotationKey: kubelettypes.FileSource,
		}
		pod.Spec.PriorityClassName = scheduling.SystemNodeCritical

		framework.ExpectEqual(kubelettypes.IsCriticalPod(pod), true, "pod should be a critical pod")
	} else {
		framework.ExpectEqual(kubelettypes.IsCriticalPod(pod), false, "pod should not be a critical pod")
	}
	return pod
}

// Emits a fake PrepareForShutdown dbus message on system dbus. Will cause kubelet to react to an active shutdown event.
func emitSignalPrepareForShutdown(b bool) error {
	cmd := "dbus-send --system /org/freedesktop/login1 org.freedesktop.login1.Manager.PrepareForShutdown boolean:" + strconv.FormatBool(b)
	_, err := runCommand("sh", "-c", cmd)
	return err
}

func getNodeReadyStatus(f *framework.Framework) bool {
	nodeList, err := f.ClientSet.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
	framework.ExpectNoError(err)
	// Assuming that there is only one node, because this is a node e2e test.
	framework.ExpectEqual(len(nodeList.Items), 1)
	return isNodeReady(&nodeList.Items[0])
}

func restartDbus() error {
	cmd := "systemctl restart dbus"
	_, err := runCommand("sh", "-c", cmd)
	return err
}

func systemctlDaemonReload() error {
	cmd := "systemctl daemon-reload"
	_, err := runCommand("sh", "-c", cmd)
	return err
}

var (
	dbusConfPath = "/etc/systemd/system/dbus.service.d/k8s-graceful-node-shutdown-e2e.conf"
	dbusConf     = `
[Unit]
RefuseManualStart=no
RefuseManualStop=no
[Service]
KillMode=control-group
ExecStop=
`
)

func overlayDbusConfig() error {
	err := os.MkdirAll(filepath.Dir(dbusConfPath), 0755)
	if err != nil {
		return err
	}
	err = os.WriteFile(dbusConfPath, []byte(dbusConf), 0644)
	if err != nil {
		return err
	}
	return systemctlDaemonReload()
}

func restoreDbusConfig() error {
	err := os.Remove(dbusConfPath)
	if err != nil {
		return err
	}
	return systemctlDaemonReload()
}
<|endoftext|>


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

package models

import (
	"encoding/json"
	"errors"
	"fmt"
	"path"
	"regexp"
	"strings"
	"time"
	"unicode"

	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/git"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/setting"
)

type ActionType int

const (
	CREATE_REPO   ActionType = iota + 1 // 1
	DELETE_REPO                         // 2
	STAR_REPO                           // 3
	FOLLOW_REPO                         // 4
	COMMIT_REPO                         // 5
	CREATE_ISSUE                        // 6
	PULL_REQUEST                        // 7
	TRANSFER_REPO                       // 8
	PUSH_TAG                            // 9
	COMMENT_ISSUE                       // 10
)

var (
	ErrNotImplemented = errors.New("Not implemented yet")
)

var (
	// Same as Github. See https://help.github.com/articles/closing-issues-via-commit-messages
	IssueKeywords    = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
	IssueKeywordsPat *regexp.Regexp
)

func init() {
	IssueKeywordsPat = regexp.MustCompile(fmt.Sprintf(`(?i)(?:%s) \S+`, strings.Join(IssueKeywords, "|")))
}

// Action represents user operation type and other information to repository.,
// it implemented interface base.Actioner so that can be used in template render.
type Action struct {
	Id           int64
	UserId       int64 // Receiver user id.
	OpType       ActionType
	ActUserId    int64  // Action user id.
	ActUserName  string // Action user name.
	ActEmail     string
	RepoId       int64
	RepoUserName string
	RepoName     string
	RefName      string
	IsPrivate    bool      `xorm:"NOT NULL DEFAULT false"`
	Content      string    `xorm:"TEXT"`
	Created      time.Time `xorm:"created"`
}

func (a Action) GetOpType() int {
	return int(a.OpType)
}

func (a Action) GetActUserName() string {
	return a.ActUserName
}

func (a Action) GetActEmail() string {
	return a.ActEmail
}

func (a Action) GetRepoUserName() string {
	return a.RepoUserName
}

func (a Action) GetRepoName() string {
	return a.RepoName
}

func (a Action) GetRepoLink() string {
	return path.Join(a.RepoUserName, a.RepoName)
}

func (a Action) GetBranch() string {
	return a.RefName
}

func (a Action) GetContent() string {
	return a.Content
}

func (a Action) GetCreate() time.Time {
	return a.Created
}

func (a Action) GetIssueInfos() []string {
	return strings.SplitN(a.Content, "|", 2)
}

func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, commits []*base.PushCommit) error {
	for _, c := range commits {
		refs := IssueKeywordsPat.FindAllString(c.Message, -1)

		for _, ref := range refs {
			ref := ref[strings.IndexByte(ref, byte(' '))+1:]
			ref = strings.TrimRightFunc(ref, func(c rune) bool {
				return !unicode.IsDigit(c)
			})

			if len(ref) == 0 {
				continue
			}

			// Add repo name if missing
			if ref[0] == '#' {
				ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
			} else if strings.Contains(ref, "/") == false {
				// We don't support User#ID syntax yet
				// return ErrNotImplemented

				continue
			}

			issue, err := GetIssueByRef(ref)

			if err != nil {
				return err
			}

			url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
			message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)

			if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil {
				return err
			}

			if issue.RepoId == repoId {
				if issue.IsClosed {
					continue
				}

				issue.IsClosed = true

				if err = UpdateIssue(issue); err != nil {
					return err
				} else if err = UpdateIssueUserPairsByStatus(issue.Id, issue.IsClosed); err != nil {
                    return err
                }

				if err = ChangeMilestoneIssueStats(issue); err != nil {
					return err
				}

				// If commit happened in the referenced repository, it means the issue can be closed.
				if _, err = CreateComment(userId, repoId, issue.Id, 0, 0, CLOSE, "", nil); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// CommitRepoAction adds new action for committing repository.
func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
	repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits, oldCommitId string, newCommitId string) error {

	opType := COMMIT_REPO
	// Check it's tag push or branch.
	if strings.HasPrefix(refFullName, "refs/tags/") {
		opType = PUSH_TAG
		commit = &base.PushCommits{}
	}

	repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
	// if not the first commit, set the compareUrl
	if !strings.HasPrefix(oldCommitId, "0000000") {
		commit.CompareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId)
	}

	bs, err := json.Marshal(commit)
	if err != nil {
		return errors.New("action.CommitRepoAction(json): " + err.Error())
	}

	refName := git.RefEndName(refFullName)

	// Change repository bare status and update last updated time.
	repo, err := GetRepositoryByName(repoUserId, repoName)
	if err != nil {
		return errors.New("action.CommitRepoAction(GetRepositoryByName): " + err.Error())
	}
	repo.IsBare = false
	if err = UpdateRepository(repo); err != nil {
		return errors.New("action.CommitRepoAction(UpdateRepository): " + err.Error())
	}

	err = updateIssuesCommit(userId, repoId, repoUserName, repoName, commit.Commits)

	if err != nil {
		log.Debug("action.CommitRepoAction(updateIssuesCommit): ", err)
	}

	if err = NotifyWatchers(&Action{ActUserId: userId, ActUserName: userName, ActEmail: actEmail,
		OpType: opType, Content: string(bs), RepoId: repoId, RepoUserName: repoUserName,
		RepoName: repoName, RefName: refName,
		IsPrivate: repo.IsPrivate}); err != nil {
		return errors.New("action.CommitRepoAction(NotifyWatchers): " + err.Error())

	}

	// New push event hook.
	if err := repo.GetOwner(); err != nil {
		return errors.New("action.CommitRepoAction(GetOwner): " + err.Error())
	}

	ws, err := GetActiveWebhooksByRepoId(repoId)
	if err != nil {
		return errors.New("action.CommitRepoAction(GetActiveWebhooksByRepoId): " + err.Error())
	}

	// check if repo belongs to org and append additional webhooks
	if repo.Owner.IsOrganization() {
		// get hooks for org
		orgws, err := GetActiveWebhooksByOrgId(repo.OwnerId)
		if err != nil {
			return errors.New("action.CommitRepoAction(GetActiveWebhooksByOrgId): " + err.Error())
		}
		ws = append(ws, orgws...)
	}

	if len(ws) == 0 {
		return nil
	}

	pusher_email, pusher_name := "", ""
	pusher, err := GetUserByName(userName)
	if err == nil {
		pusher_email = pusher.Email
		pusher_name = pusher.GetFullNameFallback()
	}

	commits := make([]*PayloadCommit, len(commit.Commits))
	for i, cmt := range commit.Commits {
		author_username := ""
		author, err := GetUserByEmail(cmt.AuthorEmail)
		if err == nil {
			author_username = author.Name
		}
		commits[i] = &PayloadCommit{
			Id:      cmt.Sha1,
			Message: cmt.Message,
			Url:     fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1),
			Author: &PayloadAuthor{
				Name:     cmt.AuthorName,
				Email:    cmt.AuthorEmail,
				UserName: author_username,
			},
		}
	}
	p := &Payload{
		Ref:     refFullName,
		Commits: commits,
		Repo: &PayloadRepo{
			Id:          repo.Id,
			Name:        repo.LowerName,
			Url:         repoLink,
			Description: repo.Description,
			Website:     repo.Website,
			Watchers:    repo.NumWatches,
			Owner: &PayloadAuthor{
				Name:     repo.Owner.GetFullNameFallback(),
				Email:    repo.Owner.Email,
				UserName: repo.Owner.Name,
			},
			Private: repo.IsPrivate,
		},
		Pusher: &PayloadAuthor{
			Name:     pusher_name,
			Email:    pusher_email,
			UserName: userName,
		},
		Before:     oldCommitId,
		After:      newCommitId,
		CompareUrl: commit.CompareUrl,
	}

	for _, w := range ws {
		w.GetEvent()
		if !w.HasPushEvent() {
			continue
		}

		switch w.HookTaskType {
		case SLACK:
			{
				s, err := GetSlackPayload(p, w.Meta)
				if err != nil {
					return errors.New("action.GetSlackPayload: " + err.Error())
				}
				CreateHookTask(&HookTask{
					Type:        w.HookTaskType,
					Url:         w.Url,
					BasePayload: s,
					ContentType: w.ContentType,
					IsSsl:       w.IsSsl,
				})
			}
		default:
			{
				p.Secret = w.Secret
				CreateHookTask(&HookTask{
					Type:        w.HookTaskType,
					Url:         w.Url,
					BasePayload: p,
					ContentType: w.ContentType,
					IsSsl:       w.IsSsl,
				})
			}
		}
	}

	go DeliverHooks()
	return nil
}

// NewRepoAction adds new action for creating repository.
func NewRepoAction(u *User, repo *Repository) (err error) {
	if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email,
		OpType: CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name,
		IsPrivate: repo.IsPrivate}); err != nil {
		log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
		return err
	}

	log.Trace("action.NewRepoAction: %s/%s", u.Name, repo.Name)
	return err
}

// TransferRepoAction adds new action for transfering repository.
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
	action := &Action{
		ActUserId:    u.Id,
		ActUserName:  u.Name,
		ActEmail:     u.Email,
		OpType:       TRANSFER_REPO,
		RepoId:       repo.Id,
		RepoUserName: newUser.Name,
		RepoName:     repo.Name,
		IsPrivate:    repo.IsPrivate,
		Content:      path.Join(repo.Owner.LowerName, repo.LowerName),
	}
	if err = NotifyWatchers(action); err != nil {
		log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
		return err
	}

	// Remove watch for organization.
	if repo.Owner.IsOrganization() {
		if err = WatchRepo(repo.Owner.Id, repo.Id, false); err != nil {
			log.Error(4, "WatchRepo", err)
		}
	}

	log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name)
	return err
}

// GetFeeds returns action list of given user in given context.
func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) {
	actions := make([]*Action, 0, 20)
	sess := x.Limit(20, int(offset)).Desc("id").Where("user_id=?", uid)
	if isProfile {
		sess.And("is_private=?", false).And("act_user_id=?", uid)
	}
	err := sess.Find(&actions)
	return actions, err
}
<commit_msg>Replace white spaces with tab for format consistency<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"encoding/json"
	"errors"
	"fmt"
	"path"
	"regexp"
	"strings"
	"time"
	"unicode"

	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/git"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/setting"
)

type ActionType int

const (
	CREATE_REPO   ActionType = iota + 1 // 1
	DELETE_REPO                         // 2
	STAR_REPO                           // 3
	FOLLOW_REPO                         // 4
	COMMIT_REPO                         // 5
	CREATE_ISSUE                        // 6
	PULL_REQUEST                        // 7
	TRANSFER_REPO                       // 8
	PUSH_TAG                            // 9
	COMMENT_ISSUE                       // 10
)

var (
	ErrNotImplemented = errors.New("Not implemented yet")
)

var (
	// Same as Github. See https://help.github.com/articles/closing-issues-via-commit-messages
	IssueKeywords    = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
	IssueKeywordsPat *regexp.Regexp
)

func init() {
	IssueKeywordsPat = regexp.MustCompile(fmt.Sprintf(`(?i)(?:%s) \S+`, strings.Join(IssueKeywords, "|")))
}

// Action represents user operation type and other information to repository.,
// it implemented interface base.Actioner so that can be used in template render.
type Action struct {
	Id           int64
	UserId       int64 // Receiver user id.
	OpType       ActionType
	ActUserId    int64  // Action user id.
	ActUserName  string // Action user name.
	ActEmail     string
	RepoId       int64
	RepoUserName string
	RepoName     string
	RefName      string
	IsPrivate    bool      `xorm:"NOT NULL DEFAULT false"`
	Content      string    `xorm:"TEXT"`
	Created      time.Time `xorm:"created"`
}

func (a Action) GetOpType() int {
	return int(a.OpType)
}

func (a Action) GetActUserName() string {
	return a.ActUserName
}

func (a Action) GetActEmail() string {
	return a.ActEmail
}

func (a Action) GetRepoUserName() string {
	return a.RepoUserName
}

func (a Action) GetRepoName() string {
	return a.RepoName
}

func (a Action) GetRepoLink() string {
	return path.Join(a.RepoUserName, a.RepoName)
}

func (a Action) GetBranch() string {
	return a.RefName
}

func (a Action) GetContent() string {
	return a.Content
}

func (a Action) GetCreate() time.Time {
	return a.Created
}

func (a Action) GetIssueInfos() []string {
	return strings.SplitN(a.Content, "|", 2)
}

func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, commits []*base.PushCommit) error {
	for _, c := range commits {
		refs := IssueKeywordsPat.FindAllString(c.Message, -1)

		for _, ref := range refs {
			ref := ref[strings.IndexByte(ref, byte(' '))+1:]
			ref = strings.TrimRightFunc(ref, func(c rune) bool {
				return !unicode.IsDigit(c)
			})

			if len(ref) == 0 {
				continue
			}

			// Add repo name if missing
			if ref[0] == '#' {
				ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
			} else if strings.Contains(ref, "/") == false {
				// We don't support User#ID syntax yet
				// return ErrNotImplemented

				continue
			}

			issue, err := GetIssueByRef(ref)

			if err != nil {
				return err
			}

			url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
			message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)

			if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil {
				return err
			}

			if issue.RepoId == repoId {
				if issue.IsClosed {
					continue
				}

				issue.IsClosed = true

				if err = UpdateIssue(issue); err != nil {
					return err
				} else if err = UpdateIssueUserPairsByStatus(issue.Id, issue.IsClosed); err != nil {
					return err
				}

				if err = ChangeMilestoneIssueStats(issue); err != nil {
					return err
				}

				// If commit happened in the referenced repository, it means the issue can be closed.
				if _, err = CreateComment(userId, repoId, issue.Id, 0, 0, CLOSE, "", nil); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// CommitRepoAction adds new action for committing repository.
func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
	repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits, oldCommitId string, newCommitId string) error {

	opType := COMMIT_REPO
	// Check it's tag push or branch.
	if strings.HasPrefix(refFullName, "refs/tags/") {
		opType = PUSH_TAG
		commit = &base.PushCommits{}
	}

	repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
	// if not the first commit, set the compareUrl
	if !strings.HasPrefix(oldCommitId, "0000000") {
		commit.CompareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId)
	}

	bs, err := json.Marshal(commit)
	if err != nil {
		return errors.New("action.CommitRepoAction(json): " + err.Error())
	}

	refName := git.RefEndName(refFullName)

	// Change repository bare status and update last updated time.
	repo, err := GetRepositoryByName(repoUserId, repoName)
	if err != nil {
		return errors.New("action.CommitRepoAction(GetRepositoryByName): " + err.Error())
	}
	repo.IsBare = false
	if err = UpdateRepository(repo); err != nil {
		return errors.New("action.CommitRepoAction(UpdateRepository): " + err.Error())
	}

	err = updateIssuesCommit(userId, repoId, repoUserName, repoName, commit.Commits)

	if err != nil {
		log.Debug("action.CommitRepoAction(updateIssuesCommit): ", err)
	}

	if err = NotifyWatchers(&Action{ActUserId: userId, ActUserName: userName, ActEmail: actEmail,
		OpType: opType, Content: string(bs), RepoId: repoId, RepoUserName: repoUserName,
		RepoName: repoName, RefName: refName,
		IsPrivate: repo.IsPrivate}); err != nil {
		return errors.New("action.CommitRepoAction(NotifyWatchers): " + err.Error())

	}

	// New push event hook.
	if err := repo.GetOwner(); err != nil {
		return errors.New("action.CommitRepoAction(GetOwner): " + err.Error())
	}

	ws, err := GetActiveWebhooksByRepoId(repoId)
	if err != nil {
		return errors.New("action.CommitRepoAction(GetActiveWebhooksByRepoId): " + err.Error())
	}

	// check if repo belongs to org and append additional webhooks
	if repo.Owner.IsOrganization() {
		// get hooks for org
		orgws, err := GetActiveWebhooksByOrgId(repo.OwnerId)
		if err != nil {
			return errors.New("action.CommitRepoAction(GetActiveWebhooksByOrgId): " + err.Error())
		}
		ws = append(ws, orgws...)
	}

	if len(ws) == 0 {
		return nil
	}

	pusher_email, pusher_name := "", ""
	pusher, err := GetUserByName(userName)
	if err == nil {
		pusher_email = pusher.Email
		pusher_name = pusher.GetFullNameFallback()
	}

	commits := make([]*PayloadCommit, len(commit.Commits))
	for i, cmt := range commit.Commits {
		author_username := ""
		author, err := GetUserByEmail(cmt.AuthorEmail)
		if err == nil {
			author_username = author.Name
		}
		commits[i] = &PayloadCommit{
			Id:      cmt.Sha1,
			Message: cmt.Message,
			Url:     fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1),
			Author: &PayloadAuthor{
				Name:     cmt.AuthorName,
				Email:    cmt.AuthorEmail,
				UserName: author_username,
			},
		}
	}
	p := &Payload{
		Ref:     refFullName,
		Commits: commits,
		Repo: &PayloadRepo{
			Id:          repo.Id,
			Name:        repo.LowerName,
			Url:         repoLink,
			Description: repo.Description,
			Website:     repo.Website,
			Watchers:    repo.NumWatches,
			Owner: &PayloadAuthor{
				Name:     repo.Owner.GetFullNameFallback(),
				Email:    repo.Owner.Email,
				UserName: repo.Owner.Name,
			},
			Private: repo.IsPrivate,
		},
		Pusher: &PayloadAuthor{
			Name:     pusher_name,
			Email:    pusher_email,
			UserName: userName,
		},
		Before:     oldCommitId,
		After:      newCommitId,
		CompareUrl: commit.CompareUrl,
	}

	for _, w := range ws {
		w.GetEvent()
		if !w.HasPushEvent() {
			continue
		}

		switch w.HookTaskType {
		case SLACK:
			{
				s, err := GetSlackPayload(p, w.Meta)
				if err != nil {
					return errors.New("action.GetSlackPayload: " + err.Error())
				}
				CreateHookTask(&HookTask{
					Type:        w.HookTaskType,
					Url:         w.Url,
					BasePayload: s,
					ContentType: w.ContentType,
					IsSsl:       w.IsSsl,
				})
			}
		default:
			{
				p.Secret = w.Secret
				CreateHookTask(&HookTask{
					Type:        w.HookTaskType,
					Url:         w.Url,
					BasePayload: p,
					ContentType: w.ContentType,
					IsSsl:       w.IsSsl,
				})
			}
		}
	}

	go DeliverHooks()
	return nil
}

// NewRepoAction adds new action for creating repository.
func NewRepoAction(u *User, repo *Repository) (err error) {
	if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email,
		OpType: CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name,
		IsPrivate: repo.IsPrivate}); err != nil {
		log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
		return err
	}

	log.Trace("action.NewRepoAction: %s/%s", u.Name, repo.Name)
	return err
}

// TransferRepoAction adds new action for transfering repository.
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
	action := &Action{
		ActUserId:    u.Id,
		ActUserName:  u.Name,
		ActEmail:     u.Email,
		OpType:       TRANSFER_REPO,
		RepoId:       repo.Id,
		RepoUserName: newUser.Name,
		RepoName:     repo.Name,
		IsPrivate:    repo.IsPrivate,
		Content:      path.Join(repo.Owner.LowerName, repo.LowerName),
	}
	if err = NotifyWatchers(action); err != nil {
		log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
		return err
	}

	// Remove watch for organization.
	if repo.Owner.IsOrganization() {
		if err = WatchRepo(repo.Owner.Id, repo.Id, false); err != nil {
			log.Error(4, "WatchRepo", err)
		}
	}

	log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name)
	return err
}

// GetFeeds returns action list of given user in given context.
func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) {
	actions := make([]*Action, 0, 20)
	sess := x.Limit(20, int(offset)).Desc("id").Where("user_id=?", uid)
	if isProfile {
		sess.And("is_private=?", false).And("act_user_id=?", uid)
	}
	err := sess.Find(&actions)
	return actions, err
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"github.com/mundipagg/boleto-api/util"
	"go.mongodb.org/mongo-driver/bson/primitive"

	"github.com/mundipagg/boleto-api/config"

	"github.com/PMoneda/flow"
	"github.com/google/uuid"

	"fmt"

	"encoding/json"
	"strconv"
)

// BoletoRequest entidade de entrada para o boleto
type BoletoRequest struct {
	Authentication Authentication `json:"authentication"`
	Agreement      Agreement      `json:"agreement"`
	Title          Title          `json:"title"`
	Recipient      Recipient      `json:"recipient"`
	Buyer          Buyer          `json:"buyer"`
	BankNumber     BankNumber     `json:"bankNumber"`
	RequestKey     string         `json:"requestKey,omitempty"`
}

// BoletoResponse entidade de saída para o boleto
type BoletoResponse struct {
	StatusCode    int    `json:"-"`
	Errors        Errors `json:"errors,omitempty"`
	ID            string `json:"id,omitempty"`
	DigitableLine string `json:"digitableLine,omitempty"`
	BarCodeNumber string `json:"barCodeNumber,omitempty"`
	OurNumber     string `json:"ourNumber,omitempty"`
	Links         []Link `json:"links,omitempty"`
}

//Link é um tipo padrão no restfull para satisfazer o HATEOAS
type Link struct {
	Href   string `json:"href,omitempty"`
	Rel    string `json:"rel,omitempty"`
	Method string `json:"method,omitempty"`
}

// BoletoView contem as informações que serão preenchidas no boleto
type BoletoView struct {
	ID            primitive.ObjectID `bson:"_id,omitempty"`
	UID           string
	SecretKey     string
	PublicKey     string        `json:"pk,omitempty"`
	Format        string        `json:"format,omitempty"`
	Boleto        BoletoRequest `json:"boleto,omitempty"`
	BankID        BankNumber    `json:"bankId,omitempty"`
	CreateDate    time.Time     `json:"createDate,omitempty"`
	BankNumber    string        `json:"bankNumber,omitempty"`
	DigitableLine string        `json:"digitableLine,omitempty"`
	OurNumber     string        `json:"ourNumber,omitempty"`
	Barcode       string        `json:"barcode,omitempty"`
	Barcode64     string        `json:"barcode64,omitempty"`
	Links         []Link        `json:"links,omitempty"`
}

// NewBoletoView cria um novo objeto view de boleto a partir de um boleto request, codigo de barras e linha digitavel
func NewBoletoView(boleto BoletoRequest, response BoletoResponse, bankName string) BoletoView {
	boleto.Authentication = Authentication{}
	uid, _ := uuid.NewUUID()
	id := primitive.NewObjectID()
	view := BoletoView{
		ID:            id,
		UID:           uid.String(),
		SecretKey:     uid.String(),
		BankID:        boleto.BankNumber,
		Boleto:        boleto,
		Barcode:       response.BarCodeNumber,
		DigitableLine: response.DigitableLine,
		OurNumber:     response.OurNumber,
		BankNumber:    boleto.BankNumber.GetBoletoBankNumberAndDigit(),
		CreateDate:    time.Now(),
	}
	view.GeneratePublicKey()
	view.Links = view.CreateLinks()
	if len(response.Links) > 0 && bankName == "BradescoShopFacil" {
		view.Links = append(view.Links, response.Links[0])
	}
	return view
}

//EncodeURL tranforma o boleto view na forma que será escrito na url
func (b *BoletoView) EncodeURL(format string) string {
	idBson := b.ID.Hex()
	url := fmt.Sprintf("%s?fmt=%s&id=%s&pk=%s", config.Get().AppURL, format, idBson, b.PublicKey)

	return url
}

//CreateLinks cria a lista de links com os formatos suportados
func (b *BoletoView) CreateLinks() []Link {
	links := make([]Link, 0, 3)
	for _, f := range []string{"html", "pdf"} {
		links = append(links, Link{Href: b.EncodeURL(f), Rel: f, Method: "GET"})
	}
	return links
}

//ToJSON tranforma o boleto view em json
func (b BoletoView) ToJSON() string {
	json, _ := json.Marshal(b)
	return string(json)
}

//ToMinifyJSON converte um model BoletoView para um JSON/STRING
func (b BoletoView) ToMinifyJSON() string {
	return util.MinifyString(b.ToJSON(), "application/json")
}

//GeneratePublicKey Gera a chave pública criptografada para geração da URL do boleto
func (b *BoletoView) GeneratePublicKey() {
	s := b.SecretKey + b.CreateDate.String() + b.Barcode + b.Boleto.Buyer.Document.Number + strconv.FormatUint(b.Boleto.Title.AmountInCents, 10)
	b.PublicKey = util.Sha256(s, "hex")
}

// BankNumber número de identificação do banco
type BankNumber int

// IsBankNumberValid verifica se o banco enviado existe
func (b BankNumber) IsBankNumberValid() bool {
	switch b {
	case BancoDoBrasil, Itau, Santander, Caixa, Bradesco, Citibank, Pefisa, Stone:
		return true
	default:
		return false
	}
}

//GetBoletoBankNumberAndDigit Retorna o numero da conta do banco do boleto
func (b BankNumber) GetBoletoBankNumberAndDigit() string {
	switch b {
	case BancoDoBrasil:
		return "001-9"
	case Caixa:
		return "104-0"
	case Santander:
		return "033-7"
	case Itau:
		return "341-7"
	case Bradesco:
		return "237-2"
	case Pefisa:
		return "174"
	case Stone:
		return "197-1"
	default:
		return ""
	}
}

const (
	// BancoDoBrasil constante do Banco do Brasil
	BancoDoBrasil = 1

	// Santander constante do Santander
	Santander = 33

	// Itau constante do Itau
	Itau = 341

	//Bradesco constante do Bradesco
	Bradesco = 237
	// Caixa constante do Caixa
	Caixa = 104

	// Citibank constante do Citi
	Citibank = 745

	//Real constante do REal
	Real = 9

	// Pefisa constante do Pefisa
	Pefisa = 174

	// Stone constante do Stone
	Stone = 197
)

// BoletoErrorConector é um connector flow para criar um objeto de erro
func BoletoErrorConector(e *flow.ExchangeMessage, u flow.URI, params ...interface{}) error {
	b := "Erro interno"
	switch t := e.GetBody().(type) {
	case error:
		b = t.Error()
	case string:
		b = t
	case *BoletoResponse:
		if len(t.Errors) > 0 {
			return nil
		}
	}

	st, err := strconv.Atoi(e.GetHeader("status"))
	if err != nil {
		st = 0
	}
	resp := BoletoResponse{}
	resp.Errors = make(Errors, 0, 0)
	resp.Errors.Append("MP"+e.GetHeader("status"), b)
	resp.StatusCode = st
	e.SetBody(resp)
	return nil
}

//HasErrors verify if Response has any error
func (b *BoletoResponse) HasErrors() bool {
	return b.Errors != nil && len(b.Errors) > 0
}

//GetBoletoResponseError Retorna um BoletoResponse com um erro específico
func GetBoletoResponseError(code, message string) BoletoResponse {
	resp := BoletoResponse{}
	resp.Errors = make(Errors, 0, 0)
	resp.Errors.Append(code, message)
	return resp
}
<commit_msg>:bug: Adiciona COMPE no boleto Citibank<commit_after>package models

import (
	"time"

	"github.com/mundipagg/boleto-api/util"
	"go.mongodb.org/mongo-driver/bson/primitive"

	"github.com/mundipagg/boleto-api/config"

	"github.com/PMoneda/flow"
	"github.com/google/uuid"

	"fmt"

	"encoding/json"
	"strconv"
)

// BoletoRequest entidade de entrada para o boleto
type BoletoRequest struct {
	Authentication Authentication `json:"authentication"`
	Agreement      Agreement      `json:"agreement"`
	Title          Title          `json:"title"`
	Recipient      Recipient      `json:"recipient"`
	Buyer          Buyer          `json:"buyer"`
	BankNumber     BankNumber     `json:"bankNumber"`
	RequestKey     string         `json:"requestKey,omitempty"`
}

// BoletoResponse entidade de saída para o boleto
type BoletoResponse struct {
	StatusCode    int    `json:"-"`
	Errors        Errors `json:"errors,omitempty"`
	ID            string `json:"id,omitempty"`
	DigitableLine string `json:"digitableLine,omitempty"`
	BarCodeNumber string `json:"barCodeNumber,omitempty"`
	OurNumber     string `json:"ourNumber,omitempty"`
	Links         []Link `json:"links,omitempty"`
}

//Link é um tipo padrão no restfull para satisfazer o HATEOAS
type Link struct {
	Href   string `json:"href,omitempty"`
	Rel    string `json:"rel,omitempty"`
	Method string `json:"method,omitempty"`
}

// BoletoView contem as informações que serão preenchidas no boleto
type BoletoView struct {
	ID            primitive.ObjectID `bson:"_id,omitempty"`
	UID           string
	SecretKey     string
	PublicKey     string        `json:"pk,omitempty"`
	Format        string        `json:"format,omitempty"`
	Boleto        BoletoRequest `json:"boleto,omitempty"`
	BankID        BankNumber    `json:"bankId,omitempty"`
	CreateDate    time.Time     `json:"createDate,omitempty"`
	BankNumber    string        `json:"bankNumber,omitempty"`
	DigitableLine string        `json:"digitableLine,omitempty"`
	OurNumber     string        `json:"ourNumber,omitempty"`
	Barcode       string        `json:"barcode,omitempty"`
	Barcode64     string        `json:"barcode64,omitempty"`
	Links         []Link        `json:"links,omitempty"`
}

// NewBoletoView cria um novo objeto view de boleto a partir de um boleto request, codigo de barras e linha digitavel
func NewBoletoView(boleto BoletoRequest, response BoletoResponse, bankName string) BoletoView {
	boleto.Authentication = Authentication{}
	uid, _ := uuid.NewUUID()
	id := primitive.NewObjectID()
	view := BoletoView{
		ID:            id,
		UID:           uid.String(),
		SecretKey:     uid.String(),
		BankID:        boleto.BankNumber,
		Boleto:        boleto,
		Barcode:       response.BarCodeNumber,
		DigitableLine: response.DigitableLine,
		OurNumber:     response.OurNumber,
		BankNumber:    boleto.BankNumber.GetBoletoBankNumberAndDigit(),
		CreateDate:    time.Now(),
	}
	view.GeneratePublicKey()
	view.Links = view.CreateLinks()
	if len(response.Links) > 0 && bankName == "BradescoShopFacil" {
		view.Links = append(view.Links, response.Links[0])
	}
	return view
}

//EncodeURL tranforma o boleto view na forma que será escrito na url
func (b *BoletoView) EncodeURL(format string) string {
	idBson := b.ID.Hex()
	url := fmt.Sprintf("%s?fmt=%s&id=%s&pk=%s", config.Get().AppURL, format, idBson, b.PublicKey)

	return url
}

//CreateLinks cria a lista de links com os formatos suportados
func (b *BoletoView) CreateLinks() []Link {
	links := make([]Link, 0, 3)
	for _, f := range []string{"html", "pdf"} {
		links = append(links, Link{Href: b.EncodeURL(f), Rel: f, Method: "GET"})
	}
	return links
}

//ToJSON tranforma o boleto view em json
func (b BoletoView) ToJSON() string {
	json, _ := json.Marshal(b)
	return string(json)
}

//ToMinifyJSON converte um model BoletoView para um JSON/STRING
func (b BoletoView) ToMinifyJSON() string {
	return util.MinifyString(b.ToJSON(), "application/json")
}

//GeneratePublicKey Gera a chave pública criptografada para geração da URL do boleto
func (b *BoletoView) GeneratePublicKey() {
	s := b.SecretKey + b.CreateDate.String() + b.Barcode + b.Boleto.Buyer.Document.Number + strconv.FormatUint(b.Boleto.Title.AmountInCents, 10)
	b.PublicKey = util.Sha256(s, "hex")
}

// BankNumber número de identificação do banco
type BankNumber int

// IsBankNumberValid verifica se o banco enviado existe
func (b BankNumber) IsBankNumberValid() bool {
	switch b {
	case BancoDoBrasil, Itau, Santander, Caixa, Bradesco, Citibank, Pefisa, Stone:
		return true
	default:
		return false
	}
}

//GetBoletoBankNumberAndDigit Retorna o numero da conta do banco do boleto
func (b BankNumber) GetBoletoBankNumberAndDigit() string {
	switch b {
	case BancoDoBrasil:
		return "001-9"
	case Caixa:
		return "104-0"
	case Citibank:
		return "745-5"
	case Santander:
		return "033-7"
	case Itau:
		return "341-7"
	case Bradesco:
		return "237-2"
	case Pefisa:
		return "174"
	case Stone:
		return "197-1"
	default:
		return ""
	}
}

const (
	// BancoDoBrasil constante do Banco do Brasil
	BancoDoBrasil = 1

	// Santander constante do Santander
	Santander = 33

	// Itau constante do Itau
	Itau = 341

	//Bradesco constante do Bradesco
	Bradesco = 237
	// Caixa constante do Caixa
	Caixa = 104

	// Citibank constante do Citi
	Citibank = 745

	//Real constante do REal
	Real = 9

	// Pefisa constante do Pefisa
	Pefisa = 174

	// Stone constante do Stone
	Stone = 197
)

// BoletoErrorConector é um connector flow para criar um objeto de erro
func BoletoErrorConector(e *flow.ExchangeMessage, u flow.URI, params ...interface{}) error {
	b := "Erro interno"
	switch t := e.GetBody().(type) {
	case error:
		b = t.Error()
	case string:
		b = t
	case *BoletoResponse:
		if len(t.Errors) > 0 {
			return nil
		}
	}

	st, err := strconv.Atoi(e.GetHeader("status"))
	if err != nil {
		st = 0
	}
	resp := BoletoResponse{}
	resp.Errors = make(Errors, 0, 0)
	resp.Errors.Append("MP"+e.GetHeader("status"), b)
	resp.StatusCode = st
	e.SetBody(resp)
	return nil
}

//HasErrors verify if Response has any error
func (b *BoletoResponse) HasErrors() bool {
	return b.Errors != nil && len(b.Errors) > 0
}

//GetBoletoResponseError Retorna um BoletoResponse com um erro específico
func GetBoletoResponseError(code, message string) BoletoResponse {
	resp := BoletoResponse{}
	resp.Errors = make(Errors, 0, 0)
	resp.Errors.Append(code, message)
	return resp
}
<|endoftext|>


<commit_before>package swgohgg

import (
	"strings"
)

func CharName(src string) string {
	switch strings.ToLower(src) {
	case "aa", "ackbar":
		return "Admiral Ackbar"
	case "ayla", "aayla":
		return "Aayla Secura"
	case "ahsoka", "asoka", "ahsoka tano":
		return "Ahsoka Tano"
	case "asaj", "asajj", "ventres", "ventress", "asajj ventress":
		return "Asajj Ventress"
	case "b2", "b2 battle droid", "b2 super battle droid":
		return "B2 Super Battle Droid"
	case "barris", "baris", "barriss", "offee", "zarris", "barriss offee":
		return "Barriss Offee"
	case "baze", "baze malbus":
		return "Baze Malbus"
	case "biggs", "biggs darklighter":
		return "Biggs Darklighter"
	case "boba", "boba fett":
		return "Boba Fett"
	case "bane", "cad", "cad bane":
		return "Cad Bane"
	case "cholo", "captain han solo":
		return "Captain Han Solo"
	case "phasma", "captain phasma":
		return "Captain Phasma"
	case "cassian", "cassian andor":
		return "Cassian Andor"
	case "cls", "commander luke", "commander luke skywalker":
		return "Commander Luke Skywalker"
	case "cody":
		return "CT-2224 \"Cody\""
	case "chirpa", "chief chirpa":
		return "Chief Chirpa"
	case "nebit", "chief nebit":
		return "Chief Nebit"
	case "chirrut":
		return "Chirrut Îmwe"
	case "sarge", "sargento":
		return "Clone Sergeant - Phase I"
	case "chewe", "chewbacca", "chewbaca":
		return "Clone Wars Chewbacca"
	case "cup", "coruscant":
		return "Coruscant Underworld Police"
	case "dooku", "dokan", "dookan", "count dooku":
		return "Count Dooku"
	case "echo":
		return "CT-21-0408 \"Echo\""
	case "fives", "5s":
		return "CT-5555 \"Fives\""
	case "rex":
		return "CT-7567 \"Rex\""
	case "maul", "darth maul":
		return "Darth Maul"
	case "sidious", "sid", "darth sidious":
		return "Darth Sidious"
	case "vader", "darth vader":
		return "Darth Vader"
	case "datcha":
		return "Dathcha"
	case "dt", "death", "death trooper":
		return "Death Trooper"
	case "dk", "krennic", "krenic", "director krenic", "director krennic":
		return "Director Krennic"
	case "eeth", "eth", "ek":
		return "Eeth Koth"
	case "palpatine", "emperor", "ep", "ip", "emperor palpatine":
		return "Emperor Palpatine"
	case "ee", "elder", "anciao", "ancião", "ewok elder":
		return "Ewok Elder"
	case "es", "ewok scout":
		return "Ewok Scout"
	case "foo":
		return "First Order Officer"
	case "fost":
		return "First Order Stormtrooper"
	case "fotp":
		return "First Order TIE Pilot"
	case "gar", "saxon", "gar saxon":
		return "Gar Saxon"
	case "gamorrean", "pig":
		return "Gamorrean Guard"
	case "gg", "grevous", "grievous", "grivous":
		return "General Grievous"
	case "kenobi", "gk", "general kenobi":
		return "General Kenobi"
	case "veers":
		return "General Veers"
	case "sg", "gs", "geonosian", "geono":
		return "Geonosian Soldier"
	case "spy", "gspy":
		return "Geonosian Spy"
	case "gat", "thrawn":
		return "Grand Admiral Thrawn"
	case "yoda", "gmy":
		return "Grand Master Yoda"
	case "tarkin", "moff":
		return "Grand Moff Tarkin"
	case "han", "solo":
		return "Han Solo"
	case "hrscout":
		return "Hoth Rebel Scount"
	case "hrsolder":
		return "Hoth Rebel Soldier"
	case "ig-100", "ig100", "ig 100":
		return "IG-100 MagnaGuard"
	case "ig-86", "ig86", "ig 86":
		return "IG-86 Sentinel Droid"
	case "ig88", "ig-88", "ig 88":
		return "IG-88"
	case "ima", "igd":
		return "Ima-Gun Di"
	case "isc":
		return "Imperial Super Commando"
	case "je", "engineer":
		return "Jawa Engineer"
	case "scavenger":
		return "Jawa Scavenger"
	case "consul", "jc", "cj":
		return "Jedi Consular"
	case "jka", "anakin":
		return "Jedi Knight Anakin"
	case "jkg":
		return "Jedi Knight Guardian"
	case "jyn":
		return "Jyn Erso"
	case "k2", "k2so":
		return "K-2SO"
	case "kit", "fisto":
		return "Kit Fisto"
	case "kylo", "ren":
		return "Kylo Ren"
	case "lando":
		return "Lando Calrissian"
	case "luke", "luke skywalker":
		return "Luke Skywalker (Farmboy)"
	case "lumi", "luminara":
		return "Luminara Unduli"
	case "mace", "windu":
		return "Mace Windu"
	case "magma":
		return "Magmatrooper"
	case "mob":
		return "Mob Enforcer"
	case "acolyte":
		return "Nightsister Acolyte"
	case "initiate":
		return "Nightsister Initiate"
	case "nute":
		return "Nute Gunray"
	case "old ben", "obi":
		return "Obi-Wan Kenobi (Old Ben)"
	case "daka":
		return "Old Daka"
	case "plo":
		return "Plo Koon"
	case "poe":
		return "Poe Dameron"
	case "poggle", "pogle":
		return "Poggle the Lesser"
	case "leia", "léia":
		return "Princess Leia"
	case "qgj", "quigon", "qui-gon":
		return "Qui-Gon Jin"
	case "r2d2", "r2":
		return "R2-D2"
	case "rp":
		return "Resistance Pilot"
	case "rt":
		return "Resistance Trooper"
	case "rg", "royal":
		return "Royal Guard"
	case "savage", "so":
		return "Savage Opress"
	case "scarif", "srp":
		return "Scarif Rebel Pathfinder"
	case "shore":
		return "Shoretrooper"
	case "snow":
		return "Snowtrooper"
	case "st":
		return "Stormtrooper"
	case "sthan", "stormtrooper han":
		return "Stormtrooper Han"
	case "sf":
		return "Sun Fac"
	case "tfp", "tie":
		return "TIE Fighter Pilot"
	case "tusken":
		return "Tusken Rider"
	case "shaman":
		return "Tusken Shaman"
	case "uror":
		return "URoRRuR'R'R"
	case "wedge":
		return "Wedge Antilles"
	case "zam", "zw":
		return "Zam Wesell"
	}
	return src
}
<commit_msg>Added new Death Trooper alias.<commit_after>package swgohgg

import (
	"strings"
)

func CharName(src string) string {
	switch strings.ToLower(src) {
	case "aa", "ackbar":
		return "Admiral Ackbar"
	case "ayla", "aayla":
		return "Aayla Secura"
	case "ahsoka", "asoka", "ahsoka tano":
		return "Ahsoka Tano"
	case "asaj", "asajj", "ventres", "ventress", "asajj ventress":
		return "Asajj Ventress"
	case "b2", "b2 battle droid", "b2 super battle droid":
		return "B2 Super Battle Droid"
	case "barris", "baris", "barriss", "offee", "zarris", "barriss offee":
		return "Barriss Offee"
	case "baze", "baze malbus":
		return "Baze Malbus"
	case "biggs", "biggs darklighter":
		return "Biggs Darklighter"
	case "boba", "boba fett":
		return "Boba Fett"
	case "bane", "cad", "cad bane":
		return "Cad Bane"
	case "cholo", "captain han solo":
		return "Captain Han Solo"
	case "phasma", "captain phasma":
		return "Captain Phasma"
	case "cassian", "cassian andor":
		return "Cassian Andor"
	case "cls", "commander luke", "commander luke skywalker":
		return "Commander Luke Skywalker"
	case "cody":
		return "CT-2224 \"Cody\""
	case "chirpa", "chief chirpa":
		return "Chief Chirpa"
	case "nebit", "chief nebit":
		return "Chief Nebit"
	case "chirrut":
		return "Chirrut Îmwe"
	case "sarge", "sargento":
		return "Clone Sergeant - Phase I"
	case "chewe", "chewbacca", "chewbaca":
		return "Clone Wars Chewbacca"
	case "cup", "coruscant":
		return "Coruscant Underworld Police"
	case "dooku", "dokan", "dookan", "count dooku":
		return "Count Dooku"
	case "echo":
		return "CT-21-0408 \"Echo\""
	case "fives", "5s":
		return "CT-5555 \"Fives\""
	case "rex":
		return "CT-7567 \"Rex\""
	case "maul", "darth maul":
		return "Darth Maul"
	case "sidious", "sid", "darth sidious":
		return "Darth Sidious"
	case "vader", "darth vader":
		return "Darth Vader"
	case "datcha":
		return "Dathcha"
	case "dt", "death", "death trooper", "deathtrooper":
		return "Death Trooper"
	case "dk", "krennic", "krenic", "director krenic", "director krennic":
		return "Director Krennic"
	case "eeth", "eth", "ek":
		return "Eeth Koth"
	case "palpatine", "emperor", "ep", "ip", "emperor palpatine":
		return "Emperor Palpatine"
	case "ee", "elder", "anciao", "ancião", "ewok elder":
		return "Ewok Elder"
	case "es", "ewok scout":
		return "Ewok Scout"
	case "foo":
		return "First Order Officer"
	case "fost":
		return "First Order Stormtrooper"
	case "fotp":
		return "First Order TIE Pilot"
	case "gar", "saxon", "gar saxon":
		return "Gar Saxon"
	case "gamorrean", "pig":
		return "Gamorrean Guard"
	case "gg", "grevous", "grievous", "grivous":
		return "General Grievous"
	case "kenobi", "gk", "general kenobi":
		return "General Kenobi"
	case "veers":
		return "General Veers"
	case "sg", "gs", "geonosian", "geono":
		return "Geonosian Soldier"
	case "spy", "gspy":
		return "Geonosian Spy"
	case "gat", "thrawn":
		return "Grand Admiral Thrawn"
	case "yoda", "gmy":
		return "Grand Master Yoda"
	case "tarkin", "moff":
		return "Grand Moff Tarkin"
	case "han", "solo":
		return "Han Solo"
	case "hrscout":
		return "Hoth Rebel Scount"
	case "hrsolder":
		return "Hoth Rebel Soldier"
	case "ig-100", "ig100", "ig 100":
		return "IG-100 MagnaGuard"
	case "ig-86", "ig86", "ig 86":
		return "IG-86 Sentinel Droid"
	case "ig88", "ig-88", "ig 88":
		return "IG-88"
	case "ima", "igd":
		return "Ima-Gun Di"
	case "isc":
		return "Imperial Super Commando"
	case "je", "engineer":
		return "Jawa Engineer"
	case "scavenger":
		return "Jawa Scavenger"
	case "consul", "jc", "cj":
		return "Jedi Consular"
	case "jka", "anakin":
		return "Jedi Knight Anakin"
	case "jkg":
		return "Jedi Knight Guardian"
	case "jyn":
		return "Jyn Erso"
	case "k2", "k2so":
		return "K-2SO"
	case "kit", "fisto":
		return "Kit Fisto"
	case "kylo", "ren":
		return "Kylo Ren"
	case "lando":
		return "Lando Calrissian"
	case "luke", "luke skywalker":
		return "Luke Skywalker (Farmboy)"
	case "lumi", "luminara":
		return "Luminara Unduli"
	case "mace", "windu":
		return "Mace Windu"
	case "magma":
		return "Magmatrooper"
	case "mob":
		return "Mob Enforcer"
	case "acolyte":
		return "Nightsister Acolyte"
	case "initiate":
		return "Nightsister Initiate"
	case "nute":
		return "Nute Gunray"
	case "old ben", "obi":
		return "Obi-Wan Kenobi (Old Ben)"
	case "daka":
		return "Old Daka"
	case "plo":
		return "Plo Koon"
	case "poe":
		return "Poe Dameron"
	case "poggle", "pogle":
		return "Poggle the Lesser"
	case "leia", "léia":
		return "Princess Leia"
	case "qgj", "quigon", "qui-gon":
		return "Qui-Gon Jin"
	case "r2d2", "r2":
		return "R2-D2"
	case "rp":
		return "Resistance Pilot"
	case "rt":
		return "Resistance Trooper"
	case "rg", "royal":
		return "Royal Guard"
	case "savage", "so":
		return "Savage Opress"
	case "scarif", "srp":
		return "Scarif Rebel Pathfinder"
	case "shore":
		return "Shoretrooper"
	case "snow":
		return "Snowtrooper"
	case "st":
		return "Stormtrooper"
	case "sthan", "stormtrooper han":
		return "Stormtrooper Han"
	case "sf":
		return "Sun Fac"
	case "tfp", "tie":
		return "TIE Fighter Pilot"
	case "tusken":
		return "Tusken Rider"
	case "shaman":
		return "Tusken Shaman"
	case "uror":
		return "URoRRuR'R'R"
	case "wedge":
		return "Wedge Antilles"
	case "zam", "zw":
		return "Zam Wesell"
	}
	return src
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/scottdware/go-junos"
	"os"
)

var (
	srx    string
	u      string
	p      string
	commit bool
)

func init() {
	flag.Usage = func() {
		fmt.Println("zone2global - Convert an SRX from a zone-based address book to a global one.")
		fmt.Println("\nUsage: zone2global [OPTIONS]")
		flag.PrintDefaults()
		os.Exit(0)
	}
	flag.StringVar(&srx, "srx", "", "SRX to run the conversion against. If specifying multiple, enclose in quotes, i.e. \"srx240-1 srx1400-2\"")
	flag.StringVar(&u, "u", "", "Username")
	flag.StringVar(&p, "p", "", "Password")
	flag.BoolVar(&commit, "commit", false, "Choose to apply the configuration instead of display it.")
	flag.Parse()
}

func main() {
	vrx := regexp.MustCompile(`(\d+)\.(\d+)([RBISX]{1})(\d+)(\.(\d+))?`)

	for _, s := range strings.Split(srx, " ") {
		jnpr, err := junos.NewSession(s, u, p)
		if err != nil {
			fmt.Println(err)
		}
		defer jnpr.Close()

		config := jnpr.ConvertAddressBook()

		if commit {
			j.Config(config, "set", true)
		}

		if !commit {
			fmt.Println(config)
		}
	}
}
<commit_msg>Added the option to create a file<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/scottdware/go-junos"
	"os"
	"strings"
)

var (
	srx    string
	u      string
	p      string
	commit bool
)

func init() {
	flag.Usage = func() {
		fmt.Println("zone2global - Convert an SRX from a zone-based address book to a global one.")
		fmt.Println("\nUsage: zone2global [OPTIONS]")
		flag.PrintDefaults()
		os.Exit(0)
	}
	flag.StringVar(&srx, "srx", "", "SRX to run the conversion against. If specifying multiple, enclose in quotes, i.e. \"srx240-1 srx1400-2\"")
	flag.StringVar(&u, "u", "", "Username")
	flag.StringVar(&p, "p", "", "Password")
	flag.BoolVar(&commit, "commit", false, "Choose to apply the configuration directly instead of creating a file.")
	flag.Parse()
}

func main() {
	for _, s := range strings.Split(srx, " ") {
		jnpr, err := junos.NewSession(s, u, p)
		if err != nil {
			fmt.Println(err)
		}
		defer jnpr.Close()

		config := jnpr.ConvertAddressBook()

		if commit {
			jnpr.Config(config, "set", true)
		}

		if !commit {
			f, err := os.Create(fmt.Sprintf("%s_globaladdrbook.txt", s))
			if err != nil {
				fmt.Println(err)
			}
			defer f.Close()

			b := bufio.NewWriter(f)

			for _, cmd := range config {
				b.Write([]byte(cmd))
			}

			b.Flush()
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package framework

import (
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"testing"
	"time"

	"github.com/pborman/uuid"

	"k8s.io/apimachinery/pkg/util/wait"
	genericapiserver "k8s.io/apiserver/pkg/server"
	genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
	client "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/util/cert"
	"k8s.io/kubernetes/cmd/kube-apiserver/app"
	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
	"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
	"k8s.io/kubernetes/pkg/master"
)

// TestServerSetup holds configuration information for a kube-apiserver test server.
type TestServerSetup struct {
	ModifyServerRunOptions func(*options.ServerRunOptions)
	ModifyServerConfig     func(*master.Config)
}

// StartTestServer runs a kube-apiserver, optionally calling out to the setup.ModifyServerRunOptions and setup.ModifyServerConfig functions
func StartTestServer(t *testing.T, stopCh <-chan struct{}, setup TestServerSetup) (client.Interface, *rest.Config) {
	certDir, _ := ioutil.TempDir("", "test-integration-"+t.Name())
	go func() {
		<-stopCh
		os.RemoveAll(certDir)
	}()

	_, defaultServiceClusterIPRange, _ := net.ParseCIDR("10.0.0.0/24")
	proxySigningKey, err := pkiutil.NewPrivateKey()
	if err != nil {
		t.Fatal(err)
	}
	proxySigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "front-proxy-ca"}, proxySigningKey)
	if err != nil {
		t.Fatal(err)
	}
	proxyCACertFile, _ := ioutil.TempFile(certDir, "proxy-ca.crt")
	if err := ioutil.WriteFile(proxyCACertFile.Name(), pkiutil.EncodeCertPEM(proxySigningCert), 0644); err != nil {
		t.Fatal(err)
	}
	clientSigningKey, err := pkiutil.NewPrivateKey()
	if err != nil {
		t.Fatal(err)
	}
	clientSigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "client-ca"}, clientSigningKey)
	if err != nil {
		t.Fatal(err)
	}
	clientCACertFile, _ := ioutil.TempFile(certDir, "client-ca.crt")
	if err := ioutil.WriteFile(clientCACertFile.Name(), pkiutil.EncodeCertPEM(clientSigningCert), 0644); err != nil {
		t.Fatal(err)
	}

	listener, _, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}

	kubeAPIServerOptions := options.NewServerRunOptions()
	kubeAPIServerOptions.SecureServing.Listener = listener
	kubeAPIServerOptions.SecureServing.BindAddress = net.ParseIP("127.0.0.1")
	kubeAPIServerOptions.SecureServing.ServerCert.CertDirectory = certDir
	kubeAPIServerOptions.InsecureServing.BindPort = 0
	kubeAPIServerOptions.Etcd.StorageConfig.Prefix = path.Join("/", uuid.New(), "registry")
	kubeAPIServerOptions.Etcd.StorageConfig.Transport.ServerList = []string{GetEtcdURL()}
	kubeAPIServerOptions.ServiceClusterIPRange = *defaultServiceClusterIPRange
	kubeAPIServerOptions.Authentication.RequestHeader.UsernameHeaders = []string{"X-Remote-User"}
	kubeAPIServerOptions.Authentication.RequestHeader.GroupHeaders = []string{"X-Remote-Group"}
	kubeAPIServerOptions.Authentication.RequestHeader.ExtraHeaderPrefixes = []string{"X-Remote-Extra-"}
	kubeAPIServerOptions.Authentication.RequestHeader.AllowedNames = []string{"kube-aggregator"}
	kubeAPIServerOptions.Authentication.RequestHeader.ClientCAFile = proxyCACertFile.Name()
	kubeAPIServerOptions.Authentication.ClientCert.ClientCA = clientCACertFile.Name()
	kubeAPIServerOptions.Authorization.Modes = []string{"Node", "RBAC"}

	if setup.ModifyServerRunOptions != nil {
		setup.ModifyServerRunOptions(kubeAPIServerOptions)
	}

	completedOptions, err := app.Complete(kubeAPIServerOptions)
	if err != nil {
		t.Fatal(err)
	}
	tunneler, proxyTransport, err := app.CreateNodeDialer(completedOptions)
	if err != nil {
		t.Fatal(err)
	}
	kubeAPIServerConfig, _, _, _, admissionPostStartHook, err := app.CreateKubeAPIServerConfig(completedOptions, tunneler, proxyTransport)
	if err != nil {
		t.Fatal(err)
	}

	if setup.ModifyServerConfig != nil {
		setup.ModifyServerConfig(kubeAPIServerConfig)
	}
	kubeAPIServer, err := app.CreateKubeAPIServer(kubeAPIServerConfig, genericapiserver.NewEmptyDelegate(), admissionPostStartHook)
	if err != nil {
		t.Fatal(err)
	}
	go func() {
		if err := kubeAPIServer.GenericAPIServer.PrepareRun().Run(stopCh); err != nil {
			t.Fatal(err)
		}
	}()

	// Adjust the loopback config for external use (external server name and CA)
	kubeAPIServerClientConfig := rest.CopyConfig(kubeAPIServerConfig.GenericConfig.LoopbackClientConfig)
	kubeAPIServerClientConfig.CAFile = path.Join(certDir, "apiserver.crt")
	kubeAPIServerClientConfig.CAData = nil
	kubeAPIServerClientConfig.ServerName = ""

	// wait for health
	err = wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
		healthzConfig := rest.CopyConfig(kubeAPIServerClientConfig)
		healthzConfig.ContentType = ""
		healthzConfig.AcceptContentTypes = ""
		kubeClient, err := client.NewForConfig(healthzConfig)
		if err != nil {
			// this happens because we race the API server start
			t.Log(err)
			return false, nil
		}

		healthStatus := 0
		kubeClient.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
		if healthStatus != http.StatusOK {
			return false, nil
		}

		return true, nil
	})
	if err != nil {
		t.Fatal(err)
	}

	kubeAPIServerClient, err := client.NewForConfig(kubeAPIServerClientConfig)
	if err != nil {
		t.Fatal(err)
	}

	return kubeAPIServerClient, kubeAPIServerClientConfig
}
<commit_msg>Ensure namespaces exist on start<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package framework

import (
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"testing"
	"time"

	"github.com/pborman/uuid"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	genericapiserver "k8s.io/apiserver/pkg/server"
	genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
	client "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/util/cert"
	"k8s.io/kubernetes/cmd/kube-apiserver/app"
	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
	"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
	"k8s.io/kubernetes/pkg/master"
)

// TestServerSetup holds configuration information for a kube-apiserver test server.
type TestServerSetup struct {
	ModifyServerRunOptions func(*options.ServerRunOptions)
	ModifyServerConfig     func(*master.Config)
}

// StartTestServer runs a kube-apiserver, optionally calling out to the setup.ModifyServerRunOptions and setup.ModifyServerConfig functions
func StartTestServer(t *testing.T, stopCh <-chan struct{}, setup TestServerSetup) (client.Interface, *rest.Config) {
	certDir, _ := ioutil.TempDir("", "test-integration-"+t.Name())
	go func() {
		<-stopCh
		os.RemoveAll(certDir)
	}()

	_, defaultServiceClusterIPRange, _ := net.ParseCIDR("10.0.0.0/24")
	proxySigningKey, err := pkiutil.NewPrivateKey()
	if err != nil {
		t.Fatal(err)
	}
	proxySigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "front-proxy-ca"}, proxySigningKey)
	if err != nil {
		t.Fatal(err)
	}
	proxyCACertFile, _ := ioutil.TempFile(certDir, "proxy-ca.crt")
	if err := ioutil.WriteFile(proxyCACertFile.Name(), pkiutil.EncodeCertPEM(proxySigningCert), 0644); err != nil {
		t.Fatal(err)
	}
	clientSigningKey, err := pkiutil.NewPrivateKey()
	if err != nil {
		t.Fatal(err)
	}
	clientSigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "client-ca"}, clientSigningKey)
	if err != nil {
		t.Fatal(err)
	}
	clientCACertFile, _ := ioutil.TempFile(certDir, "client-ca.crt")
	if err := ioutil.WriteFile(clientCACertFile.Name(), pkiutil.EncodeCertPEM(clientSigningCert), 0644); err != nil {
		t.Fatal(err)
	}

	listener, _, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}

	kubeAPIServerOptions := options.NewServerRunOptions()
	kubeAPIServerOptions.SecureServing.Listener = listener
	kubeAPIServerOptions.SecureServing.BindAddress = net.ParseIP("127.0.0.1")
	kubeAPIServerOptions.SecureServing.ServerCert.CertDirectory = certDir
	kubeAPIServerOptions.InsecureServing.BindPort = 0
	kubeAPIServerOptions.Etcd.StorageConfig.Prefix = path.Join("/", uuid.New(), "registry")
	kubeAPIServerOptions.Etcd.StorageConfig.Transport.ServerList = []string{GetEtcdURL()}
	kubeAPIServerOptions.ServiceClusterIPRange = *defaultServiceClusterIPRange
	kubeAPIServerOptions.Authentication.RequestHeader.UsernameHeaders = []string{"X-Remote-User"}
	kubeAPIServerOptions.Authentication.RequestHeader.GroupHeaders = []string{"X-Remote-Group"}
	kubeAPIServerOptions.Authentication.RequestHeader.ExtraHeaderPrefixes = []string{"X-Remote-Extra-"}
	kubeAPIServerOptions.Authentication.RequestHeader.AllowedNames = []string{"kube-aggregator"}
	kubeAPIServerOptions.Authentication.RequestHeader.ClientCAFile = proxyCACertFile.Name()
	kubeAPIServerOptions.Authentication.ClientCert.ClientCA = clientCACertFile.Name()
	kubeAPIServerOptions.Authorization.Modes = []string{"Node", "RBAC"}

	if setup.ModifyServerRunOptions != nil {
		setup.ModifyServerRunOptions(kubeAPIServerOptions)
	}

	completedOptions, err := app.Complete(kubeAPIServerOptions)
	if err != nil {
		t.Fatal(err)
	}
	tunneler, proxyTransport, err := app.CreateNodeDialer(completedOptions)
	if err != nil {
		t.Fatal(err)
	}
	kubeAPIServerConfig, _, _, _, admissionPostStartHook, err := app.CreateKubeAPIServerConfig(completedOptions, tunneler, proxyTransport)
	if err != nil {
		t.Fatal(err)
	}

	if setup.ModifyServerConfig != nil {
		setup.ModifyServerConfig(kubeAPIServerConfig)
	}
	kubeAPIServer, err := app.CreateKubeAPIServer(kubeAPIServerConfig, genericapiserver.NewEmptyDelegate(), admissionPostStartHook)
	if err != nil {
		t.Fatal(err)
	}
	go func() {
		if err := kubeAPIServer.GenericAPIServer.PrepareRun().Run(stopCh); err != nil {
			t.Fatal(err)
		}
	}()

	// Adjust the loopback config for external use (external server name and CA)
	kubeAPIServerClientConfig := rest.CopyConfig(kubeAPIServerConfig.GenericConfig.LoopbackClientConfig)
	kubeAPIServerClientConfig.CAFile = path.Join(certDir, "apiserver.crt")
	kubeAPIServerClientConfig.CAData = nil
	kubeAPIServerClientConfig.ServerName = ""

	// wait for health
	err = wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
		healthzConfig := rest.CopyConfig(kubeAPIServerClientConfig)
		healthzConfig.ContentType = ""
		healthzConfig.AcceptContentTypes = ""
		kubeClient, err := client.NewForConfig(healthzConfig)
		if err != nil {
			// this happens because we race the API server start
			t.Log(err)
			return false, nil
		}

		healthStatus := 0
		kubeClient.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
		if healthStatus != http.StatusOK {
			return false, nil
		}

		if _, err := kubeClient.CoreV1().Namespaces().Get("default", metav1.GetOptions{}); err != nil {
			return false, nil
		}
		if _, err := kubeClient.CoreV1().Namespaces().Get("kube-system", metav1.GetOptions{}); err != nil {
			return false, nil
		}

		return true, nil
	})
	if err != nil {
		t.Fatal(err)
	}

	kubeAPIServerClient, err := client.NewForConfig(kubeAPIServerClientConfig)
	if err != nil {
		t.Fatal(err)
	}

	return kubeAPIServerClient, kubeAPIServerClientConfig
}
<|endoftext|>


<commit_before>package resourcequota

import (
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"sync"
	"time"

	namespaceutil "github.com/rancher/rancher/pkg/namespace"
	"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"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/quota"
)

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) {
	validated := true
	set := getNamespaceResourceQuota(ns) != ""
	var err error
	if set {
		validated, err = isQuotaValidated(ns)
		if err != nil {
			return nil, err
		}
	}

	var limit *v3.ResourceQuotaLimit
	if validated {
		limit, err = getNamespaceLimit(ns)
		if err != nil {
			return nil, err
		}
	} else {
		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 getProjectID(ns *corev1.Namespace) string {
	if ns.Annotations != nil {
		return ns.Annotations[projectIDAnnotation]
	}
	return ""
}

func getProjectNamespaceName(projectID string) (string, string) {
	if projectID == "" {
		return "", ""
	}
	parts := strings.Split(projectID, ":")
	if len(parts) == 2 {
		return parts[0], parts[1]
	}
	return "", ""
}

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()
	isFit, msg, err := c.isQuotaFit(&finalNs, projectID, projectLimit)
	if err != nil {
		return false, err
	}
	return isFit, c.setValidated(ns, 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) isQuotaFit(ns *corev1.Namespace, projectID string, projectLimit *v3.ResourceQuotaLimit) (bool, string, error) {
	nssResourceList := api.ResourceList{}
	nsLimit, err := getNamespaceLimit(ns)
	if err != nil {
		return false, "", err
	}
	// add itself on create
	nsResourceList, err := convertLimitToResourceList(nsLimit)
	if err != nil {
		return false, "", err
	}
	nssResourceList = quota.Add(nssResourceList, nsResourceList)

	// get other Namespaces
	namespaces, err := c.NsIndexer.ByIndex(nsByProjectIndex, projectID)
	if err != nil {
		return false, "", err
	}

	for _, n := range namespaces {
		// only consider namespaces who's resource quota is set
		set, err := namespaceutil.IsNamespaceConditionSet(ns, resourceQuotaValidatedCondition, true)
		if err != nil {
			return false, "", err
		}
		if !set {
			continue
		}
		other := n.(*corev1.Namespace)
		if other.Name == ns.Name {
			continue
		}
		nsLimit, err := getNamespaceLimit(other)
		if err != nil {
			return false, "", err
		}
		nsResourceList, err := convertLimitToResourceList(nsLimit)
		if err != nil {
			return false, "", err
		}
		nssResourceList = quota.Add(nssResourceList, nsResourceList)
	}

	projectResourceList, err := convertLimitToResourceList(projectLimit)
	if err != nil {
		return false, "", err
	}

	allowed, exceeded := quota.LessThanOrEqual(nssResourceList, projectResourceList)
	if allowed {
		return true, "", nil
	}
	failedHard := quota.Mask(nssResourceList, exceeded)
	return false, fmt.Sprintf("Resource quota [%v] exceeds project limit ", prettyPrint(failedHard)), nil
}

func prettyPrint(item api.ResourceList) string {
	parts := []string{}
	keys := []string{}
	for key := range item {
		keys = append(keys, string(key))
	}
	sort.Strings(keys)
	for _, key := range keys {
		value := item[api.ResourceName(key)]
		constraint := key + "=" + value.String()
		parts = append(parts, constraint)
	}
	return strings.Join(parts, ",")
}

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>Do not create empty quota for ns<commit_after>package resourcequota

import (
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"sync"
	"time"

	namespaceutil "github.com/rancher/rancher/pkg/namespace"
	"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"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/quota"
)

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 && setDefault {
		limit = defaultResourceLimit
	}

	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 getProjectID(ns *corev1.Namespace) string {
	if ns.Annotations != nil {
		return ns.Annotations[projectIDAnnotation]
	}
	return ""
}

func getProjectNamespaceName(projectID string) (string, string) {
	if projectID == "" {
		return "", ""
	}
	parts := strings.Split(projectID, ":")
	if len(parts) == 2 {
		return parts[0], parts[1]
	}
	return "", ""
}

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()
	isFit, msg, err := c.isQuotaFit(&finalNs, projectID, projectLimit)
	if err != nil {
		return false, err
	}
	return isFit, c.setValidated(ns, 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) isQuotaFit(ns *corev1.Namespace, projectID string, projectLimit *v3.ResourceQuotaLimit) (bool, string, error) {
	nssResourceList := api.ResourceList{}
	nsLimit, err := getNamespaceLimit(ns)
	if err != nil {
		return false, "", err
	}
	// add itself on create
	nsResourceList, err := convertLimitToResourceList(nsLimit)
	if err != nil {
		return false, "", err
	}
	nssResourceList = quota.Add(nssResourceList, nsResourceList)

	// get other Namespaces
	namespaces, err := c.NsIndexer.ByIndex(nsByProjectIndex, projectID)
	if err != nil {
		return false, "", err
	}

	for _, n := range namespaces {
		// only consider namespaces who's resource quota is set
		set, err := namespaceutil.IsNamespaceConditionSet(ns, resourceQuotaValidatedCondition, true)
		if err != nil {
			return false, "", err
		}
		if !set {
			continue
		}
		other := n.(*corev1.Namespace)
		if other.Name == ns.Name {
			continue
		}
		nsLimit, err := getNamespaceLimit(other)
		if err != nil {
			return false, "", err
		}
		nsResourceList, err := convertLimitToResourceList(nsLimit)
		if err != nil {
			return false, "", err
		}
		nssResourceList = quota.Add(nssResourceList, nsResourceList)
	}

	projectResourceList, err := convertLimitToResourceList(projectLimit)
	if err != nil {
		return false, "", err
	}

	allowed, exceeded := quota.LessThanOrEqual(nssResourceList, projectResourceList)
	if allowed {
		return true, "", nil
	}
	failedHard := quota.Mask(nssResourceList, exceeded)
	return false, fmt.Sprintf("Resource quota [%v] exceeds project limit ", prettyPrint(failedHard)), nil
}

func prettyPrint(item api.ResourceList) string {
	parts := []string{}
	keys := []string{}
	for key := range item {
		keys = append(keys, string(key))
	}
	sort.Strings(keys)
	for _, key := range keys {
		value := item[api.ResourceName(key)]
		constraint := key + "=" + value.String()
		parts = append(parts, constraint)
	}
	return strings.Join(parts, ",")
}

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|>