<commit_before>package jenkins

import (
	"encoding/xml"
	"fmt"

	"bytes"
	"encoding/json"
	"github.com/pkg/errors"
	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/rancher/pkg/pipeline/utils"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/types/apis/apps/v1beta2"
	"github.com/rancher/types/apis/core/v1"
	"github.com/rancher/types/apis/project.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type Engine struct {
	JenkinsClient    *Client
	HTTPClient       *http.Client
	ServiceLister    v1.ServiceLister
	PodLister        v1.PodLister
	DeploymentLister v1beta2.DeploymentLister

	Secrets                    v1.SecretInterface
	SecretLister               v1.SecretLister
	ManagementSecretLister     v1.SecretLister
	SourceCodeCredentialLister v3.SourceCodeCredentialLister
	PipelineLister             v3.PipelineLister

	ClusterName string
	Dialer      dialer.Factory
}

func (j *Engine) getJenkinsURL(execution *v3.PipelineExecution) (string, error) {
	ns := utils.GetPipelineCommonName(execution)
	service, err := j.ServiceLister.Get(ns, utils.JenkinsName)
	if err != nil {
		return "", err
	}
	ip := service.Spec.ClusterIP
	return fmt.Sprintf("http://%s:%d", ip, utils.JenkinsPort), nil

}

func (j *Engine) PreCheck(execution *v3.PipelineExecution) (bool, error) {
	var pod *corev1.Pod
	var err error
	set := labels.Set(map[string]string{utils.LabelKeyApp: utils.JenkinsName})
	ns := utils.GetPipelineCommonName(execution)
	pods, err := j.PodLister.List(ns, set.AsSelector())
	if err != nil {
		return false, err
	}
	if len(pods) <= 0 {
		return false, errors.New("jenkins pod not found")
	}
	pod = pods[0]

	for _, cond := range pod.Status.Conditions {
		if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue {
			return true, nil
		}
	}

	return false, nil
}

func (j *Engine) getJenkinsClient(execution *v3.PipelineExecution) (*Client, error) {
	url, err := j.getJenkinsURL(execution)
	if err != nil {
		return nil, err
	}
	user := utils.PipelineSecretDefaultUser
	ns := utils.GetPipelineCommonName(execution)
	secret, err := j.SecretLister.Get(ns, utils.PipelineSecretName)
	if err != nil || secret.Data == nil {
		return nil, fmt.Errorf("error get jenkins token - %v", err)
	}
	token := string(secret.Data[utils.PipelineSecretTokenKey])

	if j.HTTPClient == nil {
		dial, err := j.Dialer.ClusterDialer(j.ClusterName)
		if err != nil {
			return nil, err
		}
		j.HTTPClient = &http.Client{
			Transport: &http.Transport{
				Dial: dial,
			},
			Timeout: 15 * time.Second,
		}
	}
	return New(url, user, token, j.HTTPClient)
}

func (j *Engine) RunPipelineExecution(execution *v3.PipelineExecution) error {
	logrus.Debug("start RunPipelineExecution")
	jobName := getJobName(execution)
	client, err := j.getJenkinsClient(execution)
	if err != nil {
		return err
	}
	_, err = client.getJobInfo(jobName)
	if e, ok := err.(*httperror.APIError); ok && e.Code.Status == http.StatusNotFound {
		if err = j.createPipelineJob(client, execution); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}
	if err := j.updatePipelineJob(client, execution); err != nil {
		return err
	}

	ns, name := ref.Parse(execution.Spec.PipelineName)
	pipeline, err := j.PipelineLister.Get(ns, name)
	if err != nil {
		return err
	}
	if err := j.setCredential(client, execution, pipeline.Spec.SourceCodeCredentialName); err != nil {
		return err
	}

	if err := j.preparePipeline(execution); err != nil {
		return err
	}
	if _, err := client.buildJob(jobName, map[string]string{}); err != nil {
		return err
	}
	return nil
}

func (j *Engine) preparePipeline(execution *v3.PipelineExecution) error {
	for _, stage := range execution.Spec.PipelineConfig.Stages {
		for _, step := range stage.Steps {
			if step.PublishImageConfig != nil {
				//prepare docker credential for publishimage step
				registry := utils.DefaultRegistry
				if step.PublishImageConfig.PushRemote && step.PublishImageConfig.Registry != "" {
					registry = step.PublishImageConfig.Registry
				} else {
					_, projectID := ref.Parse(execution.Spec.ProjectName)
					registry = fmt.Sprintf("%s.%s-pipeline", utils.LocalRegistry, projectID)
				}
				if err := j.prepareRegistryCredential(execution, registry); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func (j *Engine) prepareRegistryCredential(execution *v3.PipelineExecution, registry string) error {
	secrets, err := j.ManagementSecretLister.List(execution.Namespace, labels.Everything())
	if err != nil {
		return err
	}
	username := ""
	password := ""
	for _, s := range secrets {
		if s.Type == "kubernetes.io/dockerconfigjson" {
			m := map[string]interface{}{}
			if err := json.Unmarshal(s.Data[".dockerconfigjson"], &m); err != nil {
				return err
			}
			auths := convert.ToMapInterface(m["auths"])
			for k, v := range auths {
				if registry != k {
					//find matching registry credential
					continue
				}
				cred := convert.ToMapInterface(v)
				username = cred["username"].(string)
				password = cred["password"].(string)
			}

		}

	}

	//store dockercredential in pipeline namespace
	//TODO key-key mapping instead of registry-key mapping
	reg, _ := regexp.Compile("[^a-zA-Z0-9]+")
	proceccedRegistry := strings.ToLower(reg.ReplaceAllString(registry, ""))

	secretName := fmt.Sprintf("%s-%s", execution.Namespace, proceccedRegistry)
	logrus.Debugf("preparing registry credential %s for %s", secretName, registry)
	ns := utils.GetPipelineCommonName(execution)<commit_msg>secure string conversion for registry fields<commit_after>package jenkins

import (
	"encoding/xml"
	"fmt"

	"bytes"
	"encoding/json"
	"github.com/pkg/errors"
	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/rancher/pkg/pipeline/utils"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/types/apis/apps/v1beta2"
	"github.com/rancher/types/apis/core/v1"
	"github.com/rancher/types/apis/project.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type Engine struct {
	JenkinsClient    *Client
	HTTPClient       *http.Client
	ServiceLister    v1.ServiceLister
	PodLister        v1.PodLister
	DeploymentLister v1beta2.DeploymentLister

	Secrets                    v1.SecretInterface
	SecretLister               v1.SecretLister
	ManagementSecretLister     v1.SecretLister
	SourceCodeCredentialLister v3.SourceCodeCredentialLister
	PipelineLister             v3.PipelineLister

	ClusterName string
	Dialer      dialer.Factory
}

func (j *Engine) getJenkinsURL(execution *v3.PipelineExecution) (string, error) {
	ns := utils.GetPipelineCommonName(execution)
	service, err := j.ServiceLister.Get(ns, utils.JenkinsName)
	if err != nil {
		return "", err
	}
	ip := service.Spec.ClusterIP
	return fmt.Sprintf("http://%s:%d", ip, utils.JenkinsPort), nil

}

func (j *Engine) PreCheck(execution *v3.PipelineExecution) (bool, error) {
	var pod *corev1.Pod
	var err error
	set := labels.Set(map[string]string{utils.LabelKeyApp: utils.JenkinsName})
	ns := utils.GetPipelineCommonName(execution)
	pods, err := j.PodLister.List(ns, set.AsSelector())
	if err != nil {
		return false, err
	}
	if len(pods) <= 0 {
		return false, errors.New("jenkins pod not found")
	}
	pod = pods[0]

	for _, cond := range pod.Status.Conditions {
		if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue {
			return true, nil
		}
	}

	return false, nil
}

func (j *Engine) getJenkinsClient(execution *v3.PipelineExecution) (*Client, error) {
	url, err := j.getJenkinsURL(execution)
	if err != nil {
		return nil, err
	}
	user := utils.PipelineSecretDefaultUser
	ns := utils.GetPipelineCommonName(execution)
	secret, err := j.SecretLister.Get(ns, utils.PipelineSecretName)
	if err != nil || secret.Data == nil {
		return nil, fmt.Errorf("error get jenkins token - %v", err)
	}
	token := string(secret.Data[utils.PipelineSecretTokenKey])

	if j.HTTPClient == nil {
		dial, err := j.Dialer.ClusterDialer(j.ClusterName)
		if err != nil {
			return nil, err
		}
		j.HTTPClient = &http.Client{
			Transport: &http.Transport{
				Dial: dial,
			},
			Timeout: 15 * time.Second,
		}
	}
	return New(url, user, token, j.HTTPClient)
}

func (j *Engine) RunPipelineExecution(execution *v3.PipelineExecution) error {
	logrus.Debug("start RunPipelineExecution")
	jobName := getJobName(execution)
	client, err := j.getJenkinsClient(execution)
	if err != nil {
		return err
	}
	_, err = client.getJobInfo(jobName)
	if e, ok := err.(*httperror.APIError); ok && e.Code.Status == http.StatusNotFound {
		if err = j.createPipelineJob(client, execution); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}
	if err := j.updatePipelineJob(client, execution); err != nil {
		return err
	}

	ns, name := ref.Parse(execution.Spec.PipelineName)
	pipeline, err := j.PipelineLister.Get(ns, name)
	if err != nil {
		return err
	}
	if err := j.setCredential(client, execution, pipeline.Spec.SourceCodeCredentialName); err != nil {
		return err
	}

	if err := j.preparePipeline(execution); err != nil {
		return err
	}
	if _, err := client.buildJob(jobName, map[string]string{}); err != nil {
		return err
	}
	return nil
}

func (j *Engine) preparePipeline(execution *v3.PipelineExecution) error {
	for _, stage := range execution.Spec.PipelineConfig.Stages {
		for _, step := range stage.Steps {
			if step.PublishImageConfig != nil {
				//prepare docker credential for publishimage step
				registry := utils.DefaultRegistry
				if step.PublishImageConfig.PushRemote && step.PublishImageConfig.Registry != "" {
					registry = step.PublishImageConfig.Registry
				} else {
					_, projectID := ref.Parse(execution.Spec.ProjectName)
					registry = fmt.Sprintf("%s.%s-pipeline", utils.LocalRegistry, projectID)
				}
				if err := j.prepareRegistryCredential(execution, registry); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func (j *Engine) prepareRegistryCredential(execution *v3.PipelineExecution, registry string) error {
	secrets, err := j.ManagementSecretLister.List(execution.Namespace, labels.Everything())
	if err != nil {
		return err
	}
	username := ""
	password := ""
	for _, s := range secrets {
		if s.Type == "kubernetes.io/dockerconfigjson" {
			m := map[string]interface{}{}
			if err := json.Unmarshal(s.Data[".dockerconfigjson"], &m); err != nil {
				return err
			}
			auths := convert.ToMapInterface(m["auths"])
			for k, v := range auths {
				if registry != k {
					//find matching registry credential
					continue
				}
				cred := convert.ToMapInterface(v)
				username, _ = cred["username"].(string)
				password, _ = cred["password"].(string)
			}

		}

	}

	//store dockercredential in pipeline namespace
	//TODO key-key mapping instead of registry-key mapping
	reg, _ := regexp.Compile("[^a-zA-Z0-9]+")
	proceccedRegistry := strings.ToLower(reg.ReplaceAllString(registry, ""))

	secretName := fmt.Sprintf("%s-%s", execution.Namespace, proceccedRegistry)
	logrus.Debugf("preparing registry credential %s for %s", secretName, registry)
	ns := utils.GetPipelineCommonName(execution)
{'fraction_non_alphanumeric': 0.13369030140772087, 'fraction_numerical': 0.006218153553847482, 'mean_word_length': 3.8984771573604062, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3731286', 'n_tokens_mistral': 4332, 'n_tokens_neox': 4006, 'n_words': 846}