<commit_before>/*
Copyright (c) 2017, UPMC Enterprises
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name UPMC Enterprises nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL UPMC ENTERPRISES BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*/

package v1

import (
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ElasticsearchCluster defines the cluster
type ElasticsearchCluster struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	Type              string      `json:"type"`
	Spec              ClusterSpec `json:"spec"`
	Status            CRDStatus   `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ElasticsearchClusterList represents a list of ES Clusters
type ElasticsearchClusterList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []ElasticsearchCluster `json:"items"`
}

type CRDStatus struct {
	State   CRDState `json:"state,omitempty"`
	Message string   `json:"message,omitempty"`
}

type CRDState string

// ClusterSpec defines cluster options
type ClusterSpec struct {
	// ClientNodeReplicas defines how many client nodes to have in cluster
	ClientNodeReplicas int32 `json:"client-node-replicas"`

	// MasterNodeReplicas defines how many master nodes to have in cluster
	MasterNodeReplicas int `json:"master-node-replicas"`

	// DataNodeReplicas defines how many data nodes to have in cluster
	DataNodeReplicas int `json:"data-node-replicas"`

	// NodeSelector specifies a map of key-value pairs. For the pod to be eligible
	// to run on a node, the node must have each of the indicated key-value pairs as
	// labels.
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`

	// Tolerations specifies which tolerations the Master and Data nodes will have applied to them
	Tolerations []v1.Toleration `json:"tolerations,omitempty"`

	// Affinity (podAffinity, podAntiAffinity, nodeAffinity) will be applied to the Client nodes
	Affinity v1.Affinity `json:"affinity,omitempty"`

	// Annotations specifies a map of key-value pairs
	Annotations map[string]string `json:"Annotations,omitempty"`

	// Zones specifies a map of key-value pairs. Defines which zones
	// to deploy persistent volumes for data nodes
	Zones []string `json:"zones,omitempty"`

	// DataDiskSize specifies how large the persistent volume should be attached
	// to the data nodes in the ES cluster
	DataDiskSize string `json:"data-volume-size"`

	// ElasticSearchImage specifies the docker image to use (optional)
	ElasticSearchImage string `json:"elastic-search-image"`

	// ImagePullPolicy specifies the image-pull-policy to use (optional)
	ImagePullPolicy string `json:"image-pull-policy"`

	// Snapshot defines how snapshots are scheduled
	Snapshot Snapshot `json:"snapshot"`

	// Storage defines how volumes are provisioned
	Storage Storage `json:"storage"`

	// JavaOptions defines args passed to all elastic nodes
	JavaOptions string `json:"java-options"`

	// ClientJavaOptions defines args passed to client nodes (Overrides JavaOptions)
	ClientJavaOptions string `json:"client-java-options"`

	// DataJavaOptions defines args passed to data nodes (Overrides JavaOptions)
	DataJavaOptions string `json:"data-java-options"`

	// MasterJavaOptions defines args passed to master nodes (Overrides JavaOptions)
	MasterJavaOptions string `json:"master-java-options"`

	// ImagePullSecrets defines credentials to pull image from private repository (optional)
	ImagePullSecrets []ImagePullSecrets `json:"image-pull-secrets"`

	// Resources defines memory / cpu constraints
	Resources Resources `json:"resources"`

	// Instrumentation defines metrics for the cluster
	Instrumentation Instrumentation `json:"instrumentation"`

	// Specify how the container binds to network ports
	NetworkHost string `json:"network-host"`

	//NodePort
	NodePort int32 `json:"nodePort"`

	// Kibana
	Kibana Kibana `json:"kibana"`

	//Cerebro
	Cerebro Cerebro `json:"cerebro"`

	Scheduler Scheduler

	//KeepSecretsOnDelete tells the operator to not delete secrets when a cluster is destroyed
	KeepSecretsOnDelete bool `json:"keep-secrets-on-delete"`

	// Use SSL for clients connections
	UseSSL *bool `json:"use-ssl,omitempty"`

	// serviceAccount to use when running nodes
	ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// ImagePullSecrets defines credentials to pull image from private repository
type ImagePullSecrets struct {
	// Name defines the name of the secret file that will be used
	Name string `json:"name"`
}

// Snapshot defines all params to create / store snapshots
type Snapshot struct {
	// Enabled determines if snapshots are enabled
	SchedulerEnabled bool `json:"scheduler-enabled"`

	// RepoType defines the type of Elasticsearch Repository, s3, gcs, azure
	RepoType string `json:"type"`

	// BucketName defines the AWS s3, gcs, azure bucket/container to store snapshots
	BucketName string `json:"bucket-name"`

	// CronSchedule defines how to run the snapshots
	// SEE: https://godoc.org/github.com/robfig/cron
	CronSchedule string `json:"cron-schedule"`

	// Authentication defines credentials for snapshot requests
	Authentication Authentication `json:"authentication"`

	// Defines the image to run cronjobs
	Image string `json:"image"`

	RepoRegion string `json:"repo-region"`

	RepoAuthentication RepoAuthentication `json:"repo-authentication"`
}

type RepoAuthentication struct {
	RepoAccessKey string `json:"access-key"`
	RepoSecretKey string `json:"secret-key"`
}

// Authentication defines credentials for snapshot requests
type Authentication struct {
	UserName string `json:"username"`
	Password string `json:"password"`
}

// Storage defines how dynamic volumes are created
// https://kubernetes.io/docs/user-guide/persistent-volumes/
type Storage struct {
	// StorageType is the type of storage to create
	StorageType string `json:"type"`

	// StorageClassProvisoner is the storage provisioner type
	StorageClassProvisoner string `json:"storage-class-provisioner"`

	// StorageClass to use
	StorageClass string `json:"storage-class"`

	// Volume Reclaim Policy on Persistent Volumes
	VolumeReclaimPolicy string `json:"volume-reclaim-policy"`

	// Encrypted chooses whether or not to use encryption ("true or false")
	Encrypted string `json:"encrypted,omitempty"`
}

// Resources defines CPU / Memory restrictions on pods
type Resources struct {
	Requests MemoryCPU `json:"requests"`
	Limits   MemoryCPU `json:"limits"`
}

// MemoryCPU defines memory cpu options
type MemoryCPU struct {
	// Memory defines max amount of memory
	Memory string `json:"memory"`

	// CPU defines max amount of CPU
	CPU string `json:"cpu"`
}

// Instrumentation handles all metrics for the cluster
type Instrumentation struct {
	StatsdHost string `json:"statsd-host"`
}

// Kibana properties if wanting operator to deploy for user
type Kibana struct {
	// Defines the image to use for deploying kibana
	Image string `json:"image"`

	// ImagePullPolicy specifies the image-pull-policy to use (optional)
	ImagePullPolicy string `json:"image-pull-policy"`

	// serviceAccount to use when running kibana
	ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// Cerebro properties if wanting operator to deploy for user
type Cerebro struct {
	// Defines the image to use for deploying Cerebro
	Image string `json:"image"`

	// ImagePullPolicy specifies the image-pull-policy to use (optional)
	ImagePullPolicy string `json:"image-pull-policy"`

	Configuration string `json:"configuration"`

	// serviceAccount to use when running cerebro
	ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// Scheduler stores info about how to snapshot the cluster
type Scheduler struct {
	RepoType     string
	BucketName   string
	CronSchedule string
	Enabled      bool
	Auth         SchedulerAuthentication
	RepoAuth     RepoSchedulerAuthentication
	RepoRegion   string
	ElasticURL   string
	Namespace    string
	ClusterName  string
	Image        string
	UseSSL       bool
}

type RepoSchedulerAuthentication struct {
	RepoAccessKey string
	RepoSecretKey string
}

// SchedulerAuthentication stores credentials used to authenticate against snapshot endpoint
type SchedulerAuthentication struct {
	UserName string
	Password string
}
<commit_msg>Typo fix<commit_after>/*
Copyright (c) 2017, UPMC Enterprises
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name UPMC Enterprises nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL UPMC ENTERPRISES BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*/

package v1

import (
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ElasticsearchCluster defines the cluster
type ElasticsearchCluster struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	Type              string      `json:"type"`
	Spec              ClusterSpec `json:"spec"`
	Status            CRDStatus   `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ElasticsearchClusterList represents a list of ES Clusters
type ElasticsearchClusterList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []ElasticsearchCluster `json:"items"`
}

type CRDStatus struct {
	State   CRDState `json:"state,omitempty"`
	Message string   `json:"message,omitempty"`
}

type CRDState string

// ClusterSpec defines cluster options
type ClusterSpec struct {
	// ClientNodeReplicas defines how many client nodes to have in cluster
	ClientNodeReplicas int32 `json:"client-node-replicas"`

	// MasterNodeReplicas defines how many master nodes to have in cluster
	MasterNodeReplicas int `json:"master-node-replicas"`

	// DataNodeReplicas defines how many data nodes to have in cluster
	DataNodeReplicas int `json:"data-node-replicas"`

	// NodeSelector specifies a map of key-value pairs. For the pod to be eligible
	// to run on a node, the node must have each of the indicated key-value pairs as
	// labels.
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`

	// Tolerations specifies which tolerations the Master and Data nodes will have applied to them
	Tolerations []v1.Toleration `json:"tolerations,omitempty"`

	// Affinity (podAffinity, podAntiAffinity, nodeAffinity) will be applied to the Client nodes
	Affinity v1.Affinity `json:"affinity,omitempty"`

	// Annotations specifies a map of key-value pairs
	Annotations map[string]string `json:"annotations,omitempty"`

	// Zones specifies a map of key-value pairs. Defines which zones
	// to deploy persistent volumes for data nodes
	Zones []string `json:"zones,omitempty"`

	// DataDiskSize specifies how large the persistent volume should be attached
	// to the data nodes in the ES cluster
	DataDiskSize string `json:"data-volume-size"`

	// ElasticSearchImage specifies the docker image to use (optional)
	ElasticSearchImage string `json:"elastic-search-image"`

	// ImagePullPolicy specifies the image-pull-policy to use (optional)
	ImagePullPolicy string `json:"image-pull-policy"`

	// Snapshot defines how snapshots are scheduled
	Snapshot Snapshot `json:"snapshot"`

	// Storage defines how volumes are provisioned
	Storage Storage `json:"storage"`

	// JavaOptions defines args passed to all elastic nodes
	JavaOptions string `json:"java-options"`

	// ClientJavaOptions defines args passed to client nodes (Overrides JavaOptions)
	ClientJavaOptions string `json:"client-java-options"`

	// DataJavaOptions defines args passed to data nodes (Overrides JavaOptions)
	DataJavaOptions string `json:"data-java-options"`

	// MasterJavaOptions defines args passed to master nodes (Overrides JavaOptions)
	MasterJavaOptions string `json:"master-java-options"`

	// ImagePullSecrets defines credentials to pull image from private repository (optional)
	ImagePullSecrets []ImagePullSecrets `json:"image-pull-secrets"`

	// Resources defines memory / cpu constraints
	Resources Resources `json:"resources"`

	// Instrumentation defines metrics for the cluster
	Instrumentation Instrumentation `json:"instrumentation"`

	// Specify how the container binds to network ports
	NetworkHost string `json:"network-host"`

	//NodePort
	NodePort int32 `json:"nodePort"`

	// Kibana
	Kibana Kibana `json:"kibana"`

	//Cerebro
	Cerebro Cerebro `json:"cerebro"`

	Scheduler Scheduler

	//KeepSecretsOnDelete tells the operator to not delete secrets when a cluster is destroyed
	KeepSecretsOnDelete bool `json:"keep-secrets-on-delete"`

	// Use SSL for clients connections
	UseSSL *bool `json:"use-ssl,omitempty"`

	// serviceAccount to use when running nodes
	ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// ImagePullSecrets defines credentials to pull image from private repository
type ImagePullSecrets struct {
	// Name defines the name of the secret file that will be used
	Name string `json:"name"`
}

// Snapshot defines all params to create / store snapshots
type Snapshot struct {
	// Enabled determines if snapshots are enabled
	SchedulerEnabled bool `json:"scheduler-enabled"`

	// RepoType defines the type of Elasticsearch Repository, s3, gcs, azure
	RepoType string `json:"type"`

	// BucketName defines the AWS s3, gcs, azure bucket/container to store snapshots
	BucketName string `json:"bucket-name"`

	// CronSchedule defines how to run the snapshots
	// SEE: https://godoc.org/github.com/robfig/cron
	CronSchedule string `json:"cron-schedule"`

	// Authentication defines credentials for snapshot requests
	Authentication Authentication `json:"authentication"`

	// Defines the image to run cronjobs
	Image string `json:"image"`

	RepoRegion string `json:"repo-region"`

	RepoAuthentication RepoAuthentication `json:"repo-authentication"`
}

type RepoAuthentication struct {
	RepoAccessKey string `json:"access-key"`
	RepoSecretKey string `json:"secret-key"`
}

// Authentication defines credentials for snapshot requests
type Authentication struct {
	UserName string `json:"username"`
	Password string `json:"password"`
}

// Storage defines how dynamic volumes are created
// https://kubernetes.io/docs/user-guide/persistent-volumes/
type Storage struct {
	// StorageType is the type of storage to create
	StorageType string `json:"type"`

	// StorageClassProvisoner is the storage provisioner type
	StorageClassProvisoner string `json:"storage-class-provisioner"`

	// StorageClass to use
	StorageClass string `json:"storage-class"`

	// Volume Reclaim Policy on Persistent Volumes
	VolumeReclaimPolicy string `json:"volume-reclaim-policy"`

	// Encrypted chooses whether or not to use encryption ("true or false")
	Encrypted string `json:"encrypted,omitempty"`
}

// Resources defines CPU / Memory restrictions on pods
type Resources struct {
	Requests MemoryCPU `json:"requests"`
	Limits   MemoryCPU `json:"limits"`
}

// MemoryCPU defines memory cpu options
type MemoryCPU struct {
	// Memory defines max amount of memory
	Memory string `json:"memory"`

	// CPU defines max amount of CPU
	CPU string `json:"cpu"`
}

// Instrumentation handles all metrics for the cluster
type Instrumentation struct {
	StatsdHost string `json:"statsd-host"`
}

// Kibana properties if wanting operator to deploy for user
type Kibana struct {
	// Defines the image to use for deploying kibana
	Image string `json:"image"`

	// ImagePullPolicy specifies the image-pull-policy to use (optional)
	ImagePullPolicy string `json:"image-pull-policy"`

	// serviceAccount to use when running kibana
	ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// Cerebro properties if wanting operator to deploy for user
type Cerebro struct {
	// Defines the image to use for deploying Cerebro
	Image string `json:"image"`

	// ImagePullPolicy specifies the image-pull-policy to use (optional)
	ImagePullPolicy string `json:"image-pull-policy"`

	Configuration string `json:"configuration"`

	// serviceAccount to use when running cerebro
	ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// Scheduler stores info about how to snapshot the cluster
type Scheduler struct {
	RepoType     string
	BucketName   string
	CronSchedule string
	Enabled      bool
	Auth         SchedulerAuthentication
	RepoAuth     RepoSchedulerAuthentication
	RepoRegion   string
	ElasticURL   string
	Namespace    string
	ClusterName  string
	Image        string
	UseSSL       bool
}

type RepoSchedulerAuthentication struct {
	RepoAccessKey string
	RepoSecretKey string
}

// SchedulerAuthentication stores credentials used to authenticate against snapshot endpoint
type SchedulerAuthentication struct {
	UserName string
	Password string
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2017 OpenStack Foundation.

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 rbac

import (
	"k8s.io/api/rbac/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func GenerateRoleByNamespace(namespace string) *v1beta1.Role {
	policyRule := v1beta1.PolicyRule{
		Verbs:     []string{v1beta1.VerbAll},
		APIGroups: []string{v1beta1.APIGroupAll},
		Resources: []string{v1beta1.ResourceAll},
	}
	role := &v1beta1.Role{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Role",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      "default-role",
			Namespace: namespace,
		},
		Rules: []v1beta1.PolicyRule{policyRule},
	}
	return role
}

func GenerateRoleBinding(namespace, tenant string) *v1beta1.RoleBinding {
	subject := v1beta1.Subject{
		Kind: "Group",
		Name: tenant,
	}
	roleRef := v1beta1.RoleRef{
		APIGroup: "rbac.authorization.k8s.io",
		Kind:     "Role",
		Name:     "default-role",
	}
	roleBinding := &v1beta1.RoleBinding{
		TypeMeta: metav1.TypeMeta{
			Kind:       "RoleBinding",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      tenant + "-rolebinding",
			Namespace: namespace,
		},
		Subjects: []v1beta1.Subject{subject},
		RoleRef:  roleRef,
	}
	return roleBinding
}

func GenerateServiceAccountRoleBinding(namespace, tenant string) *v1beta1.RoleBinding {
	subject := v1beta1.Subject{
		Kind:      "ServiceAccount",
		Name:      "default",
		Namespace: namespace,
	}
	roleRef := v1beta1.RoleRef{
		APIGroup: "rbac.authorization.k8s.io",
		Kind:     "Role",
		Name:     "default-role",
	}
	roleBinding := &v1beta1.RoleBinding{
		TypeMeta: metav1.TypeMeta{
			Kind:       "RoleBinding",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      tenant + "-rolebinding-sa",
			Namespace: namespace,
		},
		Subjects: []v1beta1.Subject{subject},
		RoleRef:  roleRef,
	}
	return roleBinding
}

func GenerateClusterRole() *v1beta1.ClusterRole {
	policyRule := v1beta1.PolicyRule{
		Verbs:     []string{v1beta1.VerbAll},
		APIGroups: []string{v1beta1.APIGroupAll},
		Resources: []string{"namespaces"},
	}

	clusterRole := &v1beta1.ClusterRole{
		TypeMeta: metav1.TypeMeta{
			Kind:       "ClusterRole",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name: "namespace-creater",
		},
		Rules: []v1beta1.PolicyRule{policyRule},
	}
	return clusterRole
}

func GenerateClusterRoleBindingByTenant(tenant string) *v1beta1.ClusterRoleBinding {
	subject := v1beta1.Subject{
		Kind: "Group",
		Name: tenant,
	}
	roleRef := v1beta1.RoleRef{
		APIGroup: "rbac.authorization.k8s.io",
		Kind:     "ClusterRole",
		Name:     "namespace-creater",
	}

	clusterRoleBinding := &v1beta1.ClusterRoleBinding{
		TypeMeta: metav1.TypeMeta{
			Kind:       "ClusterRoleBinding",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name: tenant + "-namespace-creater",
		},
		Subjects: []v1beta1.Subject{subject},
		RoleRef:  roleRef,
	}
	return clusterRoleBinding
}
<commit_msg>Fix user accessing k8s pod denied<commit_after>/*
Copyright (c) 2017 OpenStack Foundation.

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 rbac

import (
	"k8s.io/api/rbac/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// GenerateRoleByNamespace generates default-role which has all the permissions in the namespace.
func GenerateRoleByNamespace(namespace string) *v1beta1.Role {
	policyRule := v1beta1.PolicyRule{
		Verbs:     []string{v1beta1.VerbAll},
		APIGroups: []string{v1beta1.APIGroupAll},
		Resources: []string{v1beta1.ResourceAll},
	}
	role := &v1beta1.Role{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Role",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      "default-role",
			Namespace: namespace,
		},
		Rules: []v1beta1.PolicyRule{policyRule},
	}
	return role
}

// GenerateRoleBinding generates rolebinding which allows user "tenant" has deault-role in the tenant namespace.
func GenerateRoleBinding(namespace, tenant string) *v1beta1.RoleBinding {
	subject := v1beta1.Subject{
		Kind: "User",
		Name: tenant,
	}
	roleRef := v1beta1.RoleRef{
		APIGroup: "rbac.authorization.k8s.io",
		Kind:     "Role",
		Name:     "default-role",
	}
	roleBinding := &v1beta1.RoleBinding{
		TypeMeta: metav1.TypeMeta{
			Kind:       "RoleBinding",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      tenant + "-rolebinding",
			Namespace: namespace,
		},
		Subjects: []v1beta1.Subject{subject},
		RoleRef:  roleRef,
	}
	return roleBinding
}

// GenerateServiceAccountRoleBinding generates rolebinding of service account in the namespace.
func GenerateServiceAccountRoleBinding(namespace, tenant string) *v1beta1.RoleBinding {
	subject := v1beta1.Subject{
		Kind:      "ServiceAccount",
		Name:      "default",
		Namespace: namespace,
	}
	roleRef := v1beta1.RoleRef{
		APIGroup: "rbac.authorization.k8s.io",
		Kind:     "Role",
		Name:     "default-role",
	}
	roleBinding := &v1beta1.RoleBinding{
		TypeMeta: metav1.TypeMeta{
			Kind:       "RoleBinding",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      tenant + "-rolebinding-sa",
			Namespace: namespace,
		},
		Subjects: []v1beta1.Subject{subject},
		RoleRef:  roleRef,
	}
	return roleBinding
}

// GenerateClusterRole generates namespace-creater ClusterRole which has the permission of namespaces resource.
func GenerateClusterRole() *v1beta1.ClusterRole {
	policyRule := v1beta1.PolicyRule{
		Verbs:     []string{v1beta1.VerbAll},
		APIGroups: []string{v1beta1.APIGroupAll},
		Resources: []string{"namespaces"},
	}

	clusterRole := &v1beta1.ClusterRole{
		TypeMeta: metav1.TypeMeta{
			Kind:       "ClusterRole",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name: "namespace-creater",
		},
		Rules: []v1beta1.PolicyRule{policyRule},
	}
	return clusterRole
}

// GenerateClusterRoleBindingByTenant generate ClusterRoleBinding which allows anyone in the "tenant" group to create namespace.
func GenerateClusterRoleBindingByTenant(tenant string) *v1beta1.ClusterRoleBinding {
	subject := v1beta1.Subject{
		Kind: "Group",
		Name: tenant,
	}
	roleRef := v1beta1.RoleRef{
		APIGroup: "rbac.authorization.k8s.io",
		Kind:     "ClusterRole",
		Name:     "namespace-creater",
	}

	clusterRoleBinding := &v1beta1.ClusterRoleBinding{
		TypeMeta: metav1.TypeMeta{
			Kind:       "ClusterRoleBinding",
			APIVersion: "rbac.authorization.k8s.io/v1beta1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name: tenant + "-namespace-creater",
		},
		Subjects: []v1beta1.Subject{subject},
		RoleRef:  roleRef,
	}
	return clusterRoleBinding
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 v1beta16

import (
	"testing"

	next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/testutil"
	yaml "gopkg.in/yaml.v2"
)

func TestUpgrade(t *testing.T) {
	yaml := `apiVersion: skaffold/v1beta16
kind: Config
build:
  artifacts:
  - image: gcr.io/k8s-skaffold/skaffold-example
    docker:
      dockerfile: path/to/Dockerfile
  - image: gcr.io/k8s-skaffold/bazel
    bazel:
      target: //mytarget
  - image: gcr.io/k8s-skaffold/jib-maven
    jib:
      args: ['-v', '--activate-profiles', 'prof']
      project: dir
  - image: gcr.io/k8s-skaffold/jib-gradle
    jib:
      args: ['-v']
  googleCloudBuild:
    projectId: test-project
test:
  - image: gcr.io/k8s-skaffold/skaffold-example
    structureTests:
     - ./test/*
deploy:
  kubectl:
    manifests:
    - k8s-*
profiles:
  - name: test profile
    build:
      artifacts:
      - image: gcr.io/k8s-skaffold/skaffold-example
        kaniko:
          buildContext:
            gcsBucket: skaffold-kaniko
          cache: {}
      cluster:
        pullSecretName: e2esecret
        namespace: default
    test:
     - image: gcr.io/k8s-skaffold/skaffold-example
       structureTests:
         - ./test/*
    deploy:
      kubectl:
        manifests:
        - k8s-*
  - name: test local
    build:
      artifacts:
      - image: gcr.io/k8s-skaffold/skaffold-example
        docker:
          dockerfile: path/to/Dockerfile
      local:
        push: false
    deploy:
      kubectl:
        manifests:
        - k8s-*
`
	expected := `apiVersion: skaffold/v1beta17
kind: Config
build:
  artifacts:
  - image: gcr.io/k8s-skaffold/skaffold-example
    docker:
      dockerfile: path/to/Dockerfile
  - image: gcr.io/k8s-skaffold/bazel
    bazel:
      target: //mytarget
  - image: gcr.io/k8s-skaffold/jib-maven
    jib:
      args: ['-v', '--activate-profiles', 'prof']
      project: dir
  - image: gcr.io/k8s-skaffold/jib-gradle
    jib:
      args: ['-v']
  googleCloudBuild:
    projectId: test-project
test:
  - image: gcr.io/k8s-skaffold/skaffold-example
    structureTests:
     - ./test/*
deploy:
  kubectl:
    manifests:
    - k8s-*
profiles:
  - name: test profile
    build:
      artifacts:
      - image: gcr.io/k8s-skaffold/skaffold-example
        kaniko:
          buildContext:
            gcsBucket: skaffold-kaniko
          cache: {}
      cluster:
        pullSecretName: e2esecret
        namespace: default
    test:
     - image: gcr.io/k8s-skaffold/skaffold-example
       structureTests:
         - ./test/*
    deploy:
      kubectl:
        manifests:
        - k8s-*
  - name: test local
    build:
      artifacts:
      - image: gcr.io/k8s-skaffold/skaffold-example
        docker:
          dockerfile: path/to/Dockerfile
      local:
        push: false
    deploy:
      kubectl:
        manifests:
        - k8s-*
`
	verifyUpgrade(t, yaml, expected)
}

func verifyUpgrade(t *testing.T, input, output string) {
	config := NewSkaffoldConfig()
	err := yaml.UnmarshalStrict([]byte(input), config)
	testutil.CheckErrorAndDeepEqual(t, false, err, Version, config.GetVersion())

	upgraded, err := config.Upgrade()
	testutil.CheckError(t, false, err)

	expected := next.NewSkaffoldConfig()
	err = yaml.UnmarshalStrict([]byte(output), expected)

	testutil.CheckErrorAndDeepEqual(t, false, err, expected, upgraded)
}
<commit_msg>fix linter<commit_after>/*
Copyright 2019 The Skaffold 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 v1beta16

import (
	"testing"

	yaml "gopkg.in/yaml.v2"

	next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/testutil"
)

func TestUpgrade(t *testing.T) {
	yaml := `apiVersion: skaffold/v1beta16
kind: Config
build:
  artifacts:
  - image: gcr.io/k8s-skaffold/skaffold-example
    docker:
      dockerfile: path/to/Dockerfile
  - image: gcr.io/k8s-skaffold/bazel
    bazel:
      target: //mytarget
  - image: gcr.io/k8s-skaffold/jib-maven
    jib:
      args: ['-v', '--activate-profiles', 'prof']
      project: dir
  - image: gcr.io/k8s-skaffold/jib-gradle
    jib:
      args: ['-v']
  googleCloudBuild:
    projectId: test-project
test:
  - image: gcr.io/k8s-skaffold/skaffold-example
    structureTests:
     - ./test/*
deploy:
  kubectl:
    manifests:
    - k8s-*
profiles:
  - name: test profile
    build:
      artifacts:
      - image: gcr.io/k8s-skaffold/skaffold-example
        kaniko:
          buildContext:
            gcsBucket: skaffold-kaniko
          cache: {}
      cluster:
        pullSecretName: e2esecret
        namespace: default
    test:
     - image: gcr.io/k8s-skaffold/skaffold-example
       structureTests:
         - ./test/*
    deploy:
      kubectl:
        manifests:
        - k8s-*
  - name: test local
    build:
      artifacts:
      - image: gcr.io/k8s-skaffold/skaffold-example
        docker:
          dockerfile: path/to/Dockerfile
      local:
        push: false
    deploy:
      kubectl:
        manifests:
        - k8s-*
`
	expected := `apiVersion: skaffold/v1beta17
kind: Config
build:
  artifacts:
  - image: gcr.io/k8s-skaffold/skaffold-example
    docker:
      dockerfile: path/to/Dockerfile
  - image: gcr.io/k8s-skaffold/bazel
    bazel:
      target: //mytarget
  - image: gcr.io/k8s-skaffold/jib-maven
    jib:
      args: ['-v', '--activate-profiles', 'prof']
      project: dir
  - image: gcr.io/k8s-skaffold/jib-gradle
    jib:
      args: ['-v']
  googleCloudBuild:
    projectId: test-project
test:
  - image: gcr.io/k8s-skaffold/skaffold-example
    structureTests:
     - ./test/*
deploy:
  kubectl:
    manifests:
    - k8s-*
profiles:
  - name: test profile
    build:
      artifacts:
      - image: gcr.io/k8s-skaffold/skaffold-example
        kaniko:
          buildContext:
            gcsBucket: skaffold-kaniko
          cache: {}
      cluster:
        pullSecretName: e2esecret
        namespace: default
    test:
     - image: gcr.io/k8s-skaffold/skaffold-example
       structureTests:
         - ./test/*
    deploy:
      kubectl:
        manifests:
        - k8s-*
  - name: test local
    build:
      artifacts:
      - image: gcr.io/k8s-skaffold/skaffold-example
        docker:
          dockerfile: path/to/Dockerfile
      local:
        push: false
    deploy:
      kubectl:
        manifests:
        - k8s-*
`
	verifyUpgrade(t, yaml, expected)
}

func verifyUpgrade(t *testing.T, input, output string) {
	config := NewSkaffoldConfig()
	err := yaml.UnmarshalStrict([]byte(input), config)
	testutil.CheckErrorAndDeepEqual(t, false, err, Version, config.GetVersion())

	upgraded, err := config.Upgrade()
	testutil.CheckError(t, false, err)

	expected := next.NewSkaffoldConfig()
	err = yaml.UnmarshalStrict([]byte(output), expected)

	testutil.CheckErrorAndDeepEqual(t, false, err, expected, upgraded)
}
<|endoftext|>


<commit_before>package language

import (
	"strconv"
	"gopkg.in/ldap.v2"
	"fmt"
	"github.com/Frostman/aptomi/pkg/slinga/language/yaml"
	. "github.com/Frostman/aptomi/pkg/slinga/db"
	. "github.com/Frostman/aptomi/pkg/slinga/util"
	. "github.com/Frostman/aptomi/pkg/slinga/log"
	log "github.com/Sirupsen/logrus"
	"github.com/mattn/go-zglob"
)

type LDAPConfig struct {
	Host              string
	Port              int
	BaseDN            string
	Filter            string
	LabelToAtrributes map[string]string
}

// Loads LDAP configuration
func loadLDAPConfig(baseDir string) *LDAPConfig {
	files, _ := zglob.Glob(GetAptomiObjectFilePatternYaml(baseDir, TypeUsersLDAP))
	fileName, err := EnsureSingleFile(files)
	if err != nil {
		Debug.WithFields(log.Fields{
			"error": err,
		}).Panic("LDAP config lookup error")
	}
	result := yaml.LoadObjectFromFile(fileName, &LDAPConfig{}).(*LDAPConfig)

	Debug.WithFields(log.Fields{
		"config": fmt.Sprintf("%v", result),
	}).Info("Loaded LDAP config and mappings")

	return result
}

// Returns the list of attributes to be retrieved from LDAP
func (config *LDAPConfig) getAttributes() []string {
	result := []string{}
	for _, attr := range config.LabelToAtrributes {
		result = append(result, attr)
	}
	return result
}

// UserLoaderFromLDAP allows aptomi to load users from LDAP
type UserLoaderFromLDAP struct {
	config      *LDAPConfig
	cachedUsers *GlobalUsers
}

// NewUserLoaderFromLDAP returns new UserLoaderFromLDAP, given location with LDAP configuration file (with host/port and mapping)
func NewUserLoaderFromLDAP(baseDir string) UserLoader {
	return &UserLoaderFromLDAP{config: loadLDAPConfig(baseDir)}
}

// LoadUsersAll loads all users
func (loader *UserLoaderFromLDAP) LoadUsersAll() GlobalUsers {
	if loader.cachedUsers == nil {
		loader.cachedUsers = &GlobalUsers{Users: make(map[string]*User)}
		t := loader.ldapSearch()
		for _, u := range t {
			// load secrets
			u.Secrets = LoadUserSecretsByIDFromDir(GetAptomiPolicyDir(), u.ID)

			// add user
			loader.cachedUsers.Users[u.ID] = u
		}

	}
	return *loader.cachedUsers
}

// LoadUserByID loads a single user by ID
func (loader *UserLoaderFromLDAP) LoadUserByID(id string) *User {
	return loader.LoadUsersAll().Users[id]
}

// Summary returns summary as string
func (loader *UserLoaderFromLDAP) Summary() string {
	return strconv.Itoa(len(loader.LoadUsersAll().Users)) + " (from LDAP)"
}

// Does search on LDAP and returns entries
func (loader *UserLoaderFromLDAP) ldapSearch() []*User {
	Debug.WithFields(log.Fields{
		"host": loader.config.Host,
		"post": loader.config.Port,
	}).Info("Opening connection to LDAP")

	l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", loader.config.Host, loader.config.Port))
	if err != nil {
		panic(err)
	}
	defer l.Close()

	searchRequest := ldap.NewSearchRequest(
		loader.config.BaseDN,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		loader.config.Filter,
		loader.config.getAttributes(),
		nil,
	)

	Debug.WithFields(log.Fields{
		"baseDN":     loader.config.BaseDN,
		"filter":     loader.config.Filter,
		"attributes": loader.config.getAttributes(),
	}).Info("Making search request to LDAP")

	searchResult, err := l.Search(searchRequest)
	if err != nil {
		panic(err)
	}

	Debug.WithFields(log.Fields{
		"count": len(searchResult.Entries),
	}).Info("Found entries")

	result := []*User{}
	for _, entry := range searchResult.Entries {
		user := &User{
			ID:     entry.DN,
			Name:   entry.GetAttributeValue(loader.config.LabelToAtrributes["name"]),
			Labels: make(map[string]string),
		}
		for label, attr := range loader.config.LabelToAtrributes {
			if label != "id" && label != "name" {
				user.Labels[label] = entry.GetAttributeValue(attr)
			}
		}

		// fmt.Printf("%+v\n", user)
		result = append(result, user)
	}

	return result
}
<commit_msg>don't add empty attributes if they are empty or not present in LDAP<commit_after>package language

import (
	"strconv"
	"gopkg.in/ldap.v2"
	"fmt"
	"github.com/Frostman/aptomi/pkg/slinga/language/yaml"
	. "github.com/Frostman/aptomi/pkg/slinga/db"
	. "github.com/Frostman/aptomi/pkg/slinga/util"
	. "github.com/Frostman/aptomi/pkg/slinga/log"
	log "github.com/Sirupsen/logrus"
	"github.com/mattn/go-zglob"
)

type LDAPConfig struct {
	Host              string
	Port              int
	BaseDN            string
	Filter            string
	LabelToAtrributes map[string]string
}

// Loads LDAP configuration
func loadLDAPConfig(baseDir string) *LDAPConfig {
	files, _ := zglob.Glob(GetAptomiObjectFilePatternYaml(baseDir, TypeUsersLDAP))
	fileName, err := EnsureSingleFile(files)
	if err != nil {
		Debug.WithFields(log.Fields{
			"error": err,
		}).Panic("LDAP config lookup error")
	}
	result := yaml.LoadObjectFromFile(fileName, &LDAPConfig{}).(*LDAPConfig)

	Debug.WithFields(log.Fields{
		"config": fmt.Sprintf("%v", result),
	}).Info("Loaded LDAP config and mappings")

	return result
}

// Returns the list of attributes to be retrieved from LDAP
func (config *LDAPConfig) getAttributes() []string {
	result := []string{}
	for _, attr := range config.LabelToAtrributes {
		result = append(result, attr)
	}
	return result
}

// UserLoaderFromLDAP allows aptomi to load users from LDAP
type UserLoaderFromLDAP struct {
	config      *LDAPConfig
	cachedUsers *GlobalUsers
}

// NewUserLoaderFromLDAP returns new UserLoaderFromLDAP, given location with LDAP configuration file (with host/port and mapping)
func NewUserLoaderFromLDAP(baseDir string) UserLoader {
	return &UserLoaderFromLDAP{config: loadLDAPConfig(baseDir)}
}

// LoadUsersAll loads all users
func (loader *UserLoaderFromLDAP) LoadUsersAll() GlobalUsers {
	if loader.cachedUsers == nil {
		loader.cachedUsers = &GlobalUsers{Users: make(map[string]*User)}
		t := loader.ldapSearch()
		for _, u := range t {
			// load secrets
			u.Secrets = LoadUserSecretsByIDFromDir(GetAptomiPolicyDir(), u.ID)

			// add user
			loader.cachedUsers.Users[u.ID] = u
		}

	}
	return *loader.cachedUsers
}

// LoadUserByID loads a single user by ID
func (loader *UserLoaderFromLDAP) LoadUserByID(id string) *User {
	return loader.LoadUsersAll().Users[id]
}

// Summary returns summary as string
func (loader *UserLoaderFromLDAP) Summary() string {
	return strconv.Itoa(len(loader.LoadUsersAll().Users)) + " (from LDAP)"
}

// Does search on LDAP and returns entries
func (loader *UserLoaderFromLDAP) ldapSearch() []*User {
	Debug.WithFields(log.Fields{
		"host": loader.config.Host,
		"post": loader.config.Port,
	}).Info("Opening connection to LDAP")

	l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", loader.config.Host, loader.config.Port))
	if err != nil {
		panic(err)
	}
	defer l.Close()

	searchRequest := ldap.NewSearchRequest(
		loader.config.BaseDN,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		loader.config.Filter,
		loader.config.getAttributes(),
		nil,
	)

	Debug.WithFields(log.Fields{
		"baseDN":     loader.config.BaseDN,
		"filter":     loader.config.Filter,
		"attributes": loader.config.getAttributes(),
	}).Info("Making search request to LDAP")

	searchResult, err := l.Search(searchRequest)
	if err != nil {
		panic(err)
	}

	Debug.WithFields(log.Fields{
		"count": len(searchResult.Entries),
	}).Info("Found entries")

	result := []*User{}
	for _, entry := range searchResult.Entries {
		user := &User{
			ID:     entry.DN,
			Name:   entry.GetAttributeValue(loader.config.LabelToAtrributes["name"]),
			Labels: make(map[string]string),
		}
		for label, attr := range loader.config.LabelToAtrributes {
			if label != "id" && label != "name" {
				value := entry.GetAttributeValue(attr)
				if len(value) > 0 {
					user.Labels[label] = entry.GetAttributeValue(attr)
				}
			}
		}

		// fmt.Printf("%+v\n", user)
		result = append(result, user)
	}

	return result
}
<|endoftext|>


<commit_before>package ast

import (
	"fmt"
	"strings"

	textm "github.com/yuin/goldmark/text"
)

// A BaseBlock struct implements the Node interface partialliy.
type BaseBlock struct {
	BaseNode
	blankPreviousLines bool
	lines              *textm.Segments
}

// Type implements Node.Type
func (b *BaseBlock) Type() NodeType {
	return TypeBlock
}

// IsRaw implements Node.IsRaw
func (b *BaseBlock) IsRaw() bool {
	return false
}

// HasBlankPreviousLines implements Node.HasBlankPreviousLines.
func (b *BaseBlock) HasBlankPreviousLines() bool {
	return b.blankPreviousLines
}

// SetBlankPreviousLines implements Node.SetBlankPreviousLines.
func (b *BaseBlock) SetBlankPreviousLines(v bool) {
	b.blankPreviousLines = v
}

// Lines implements Node.Lines
func (b *BaseBlock) Lines() *textm.Segments {
	if b.lines == nil {
		b.lines = textm.NewSegments()
	}
	return b.lines
}

// SetLines implements Node.SetLines
func (b *BaseBlock) SetLines(v *textm.Segments) {
	b.lines = v
}

// A Document struct is a root node of Markdown text.
type Document struct {
	BaseBlock

	meta map[string]interface{}
}

// KindDocument is a NodeKind of the Document node.
var KindDocument = NewNodeKind("Document")

// Dump implements Node.Dump .
func (n *Document) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// Type implements Node.Type .
func (n *Document) Type() NodeType {
	return TypeDocument
}

// Kind implements Node.Kind.
func (n *Document) Kind() NodeKind {
	return KindDocument
}

// OwnerDocument implements Node.OwnerDocument
func (n *Document) OwnerDocument() *Document {
	return n
}

// Meta returns metadata of this document.
func (n *Document) Meta() map[string]interface{} {
	if n.meta == nil {
		n.meta = map[string]interface{}{}
	}
	return n.meta
}

// SetMeta sets given metadata to this document.
func (n *Document) SetMeta(meta map[string]interface{}) {
	n.meta = meta
}

// NewDocument returns a new Document node.
func NewDocument() *Document {
	return &Document{
		BaseBlock: BaseBlock{},
		meta:      nil,
	}
}

// A TextBlock struct is a node whose lines
// should be rendered without any containers.
type TextBlock struct {
	BaseBlock
}

// Dump implements Node.Dump .
func (n *TextBlock) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindTextBlock is a NodeKind of the TextBlock node.
var KindTextBlock = NewNodeKind("TextBlock")

// Kind implements Node.Kind.
func (n *TextBlock) Kind() NodeKind {
	return KindTextBlock
}

// NewTextBlock returns a new TextBlock node.
func NewTextBlock() *TextBlock {
	return &TextBlock{
		BaseBlock: BaseBlock{},
	}
}

// A Paragraph struct represents a paragraph of Markdown text.
type Paragraph struct {
	BaseBlock
}

// Dump implements Node.Dump .
func (n *Paragraph) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindParagraph is a NodeKind of the Paragraph node.
var KindParagraph = NewNodeKind("Paragraph")

// Kind implements Node.Kind.
func (n *Paragraph) Kind() NodeKind {
	return KindParagraph
}

// NewParagraph returns a new Paragraph node.
func NewParagraph() *Paragraph {
	return &Paragraph{
		BaseBlock: BaseBlock{},
	}
}

// IsParagraph returns true if the given node implements the Paragraph interface,
// otherwise false.
func IsParagraph(node Node) bool {
	_, ok := node.(*Paragraph)
	return ok
}

// A Heading struct represents headings like SetextHeading and ATXHeading.
type Heading struct {
	BaseBlock
	// Level returns a level of this heading.
	// This value is between 1 and 6.
	Level int
}

// Dump implements Node.Dump .
func (n *Heading) Dump(source []byte, level int) {
	m := map[string]string{
		"Level": fmt.Sprintf("%d", n.Level),
	}
	DumpHelper(n, source, level, m, nil)
}

// KindHeading is a NodeKind of the Heading node.
var KindHeading = NewNodeKind("Heading")

// Kind implements Node.Kind.
func (n *Heading) Kind() NodeKind {
	return KindHeading
}

// NewHeading returns a new Heading node.
func NewHeading(level int) *Heading {
	return &Heading{
		BaseBlock: BaseBlock{},
		Level:     level,
	}
}

// A ThematicBreak struct represents a thematic break of Markdown text.
type ThematicBreak struct {
	BaseBlock
}

// Dump implements Node.Dump .
func (n *ThematicBreak) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindThematicBreak is a NodeKind of the ThematicBreak node.
var KindThematicBreak = NewNodeKind("ThematicBreak")

// Kind implements Node.Kind.
func (n *ThematicBreak) Kind() NodeKind {
	return KindThematicBreak
}

// NewThematicBreak returns a new ThematicBreak node.
func NewThematicBreak() *ThematicBreak {
	return &ThematicBreak{
		BaseBlock: BaseBlock{},
	}
}

// A CodeBlock interface represents an indented code block of Markdown text.
type CodeBlock struct {
	BaseBlock
}

// IsRaw implements Node.IsRaw.
func (n *CodeBlock) IsRaw() bool {
	return true
}

// Dump implements Node.Dump .
func (n *CodeBlock) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindCodeBlock is a NodeKind of the CodeBlock node.
var KindCodeBlock = NewNodeKind("CodeBlock")

// Kind implements Node.Kind.
func (n *CodeBlock) Kind() NodeKind {
	return KindCodeBlock
}

// NewCodeBlock returns a new CodeBlock node.
func NewCodeBlock() *CodeBlock {
	return &CodeBlock{
		BaseBlock: BaseBlock{},
	}
}

// A FencedCodeBlock struct represents a fenced code block of Markdown text.
type FencedCodeBlock struct {
	BaseBlock
	// Info returns a info text of this fenced code block.
	Info *Text

	language []byte
}

// Language returns an language in an info string.
// Language returns nil if this node does not have an info string.
func (n *FencedCodeBlock) Language(source []byte) []byte {
	if n.language == nil && n.Info != nil {
		segment := n.Info.Segment
		info := segment.Value(source)
		i := 0
		for ; i < len(info); i++ {
			if info[i] == ' ' {
				break
			}
		}
		n.language = info[:i]
	}
	return n.language
}

// IsRaw implements Node.IsRaw.
func (n *FencedCodeBlock) IsRaw() bool {
	return true
}

// Dump implements Node.Dump .
func (n *FencedCodeBlock) Dump(source []byte, level int) {
	m := map[string]string{}
	if n.Info != nil {
		m["Info"] = fmt.Sprintf("\"%s\"", n.Info.Text(source))
	}
	DumpHelper(n, source, level, m, nil)
}

// KindFencedCodeBlock is a NodeKind of the FencedCodeBlock node.
var KindFencedCodeBlock = NewNodeKind("FencedCodeBlock")

// Kind implements Node.Kind.
func (n *FencedCodeBlock) Kind() NodeKind {
	return KindFencedCodeBlock
}

// NewFencedCodeBlock return a new FencedCodeBlock node.
func NewFencedCodeBlock(info *Text) *FencedCodeBlock {
	return &FencedCodeBlock{
		BaseBlock: BaseBlock{},
		Info:      info,
	}
}

// A Blockquote struct represents an blockquote block of Markdown text.
type Blockquote struct {
	BaseBlock
}

// Dump implements Node.Dump .
func (n *Blockquote) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindBlockquote is a NodeKind of the Blockquote node.
var KindBlockquote = NewNodeKind("Blockquote")

// Kind implements Node.Kind.
func (n *Blockquote) Kind() NodeKind {
	return KindBlockquote
}

// NewBlockquote returns a new Blockquote node.
func NewBlockquote() *Blockquote {
	return &Blockquote{
		BaseBlock: BaseBlock{},
	}
}

// A List struct represents a list of Markdown text.
type List struct {
	BaseBlock

	// Marker is a marker character like '-', '+', ')' and '.'.
	Marker byte

	// IsTight is a true if this list is a 'tight' list.
	// See https://spec.commonmark.org/0.30/#loose for details.
	IsTight bool

	// Start is an initial number of this ordered list.
	// If this list is not an ordered list, Start is 0.
	Start int
}

// IsOrdered returns true if this list is an ordered list, otherwise false.
func (l *List) IsOrdered() bool {
	return l.Marker == '.' || l.Marker == ')'
}

// CanContinue returns true if this list can continue with
// the given mark and a list type, otherwise false.
func (l *List) CanContinue(marker byte, isOrdered bool) bool {
	return marker == l.Marker && isOrdered == l.IsOrdered()
}

// Dump implements Node.Dump.
func (l *List) Dump(source []byte, level int) {
	m := map[string]string{
		"Ordered": fmt.Sprintf("%v", l.IsOrdered()),
		"Marker":  fmt.Sprintf("%c", l.Marker),
		"Tight":   fmt.Sprintf("%v", l.IsTight),
	}
	if l.IsOrdered() {
		m["Start"] = fmt.Sprintf("%d", l.Start)
	}
	DumpHelper(l, source, level, m, nil)
}

// KindList is a NodeKind of the List node.
var KindList = NewNodeKind("List")

// Kind implements Node.Kind.
func (l *List) Kind() NodeKind {
	return KindList
}

// NewList returns a new List node.
func NewList(marker byte) *List {
	return &List{
		BaseBlock: BaseBlock{},
		Marker:    marker,
		IsTight:   true,
	}
}

// A ListItem struct represents a list item of Markdown text.
type ListItem struct {
	BaseBlock

	// Offset is an offset position of this item.
	Offset int
}

// Dump implements Node.Dump.
func (n *ListItem) Dump(source []byte, level int) {
	m := map[string]string{
		"Offset": fmt.Sprintf("%d", n.Offset),
	}
	DumpHelper(n, source, level, m, nil)
}

// KindListItem is a NodeKind of the ListItem node.
var KindListItem = NewNodeKind("ListItem")

// Kind implements Node.Kind.
func (n *ListItem) Kind() NodeKind {
	return KindListItem
}

// NewListItem returns a new ListItem node.
func NewListItem(offset int) *ListItem {
	return &ListItem{
		BaseBlock: BaseBlock{},
		Offset:    offset,
	}
}

// HTMLBlockType represents kinds of an html blocks.
// See https://spec.commonmark.org/0.30/#html-blocks
type HTMLBlockType int

const (
	// HTMLBlockType1 represents type 1 html blocks
	HTMLBlockType1 HTMLBlockType = iota + 1
	// HTMLBlockType2 represents type 2 html blocks
	HTMLBlockType2
	// HTMLBlockType3 represents type 3 html blocks
	HTMLBlockType3
	// HTMLBlockType4 represents type 4 html blocks
	HTMLBlockType4
	// HTMLBlockType5 represents type 5 html blocks
	HTMLBlockType5
	// HTMLBlockType6 represents type 6 html blocks
	HTMLBlockType6
	// HTMLBlockType7 represents type 7 html blocks
	HTMLBlockType7
)

// An HTMLBlock struct represents an html block of Markdown text.
type HTMLBlock struct {
	BaseBlock

	// Type is a type of this html block.
	HTMLBlockType HTMLBlockType

	// ClosureLine is a line that closes this html block.
	ClosureLine textm.Segment
}

// IsRaw implements Node.IsRaw.
func (n *HTMLBlock) IsRaw() bool {
	return true
}

// HasClosure returns true if this html block has a closure line,
// otherwise false.
func (n *HTMLBlock) HasClosure() bool {
	return n.ClosureLine.Start >= 0
}

// Dump implements Node.Dump.
func (n *HTMLBlock) Dump(source []byte, level int) {
	indent := strings.Repeat("    ", level)
	fmt.Printf("%s%s {\n", indent, "HTMLBlock")
	indent2 := strings.Repeat("    ", level+1)
	fmt.Printf("%sRawText: \"", indent2)
	for i := 0; i < n.Lines().Len(); i++ {
		s := n.Lines().At(i)
		fmt.Print(string(source[s.Start:s.Stop]))
	}
	fmt.Printf("\"\n")
	for c := n.FirstChild(); c != nil; c = c.NextSibling() {
		c.Dump(source, level+1)
	}
	if n.HasClosure() {
		cl := n.ClosureLine
		fmt.Printf("%sClosure: \"%s\"\n", indent2, string(cl.Value(source)))
	}
	fmt.Printf("%s}\n", indent)
}

// KindHTMLBlock is a NodeKind of the HTMLBlock node.
var KindHTMLBlock = NewNodeKind("HTMLBlock")

// Kind implements Node.Kind.
func (n *HTMLBlock) Kind() NodeKind {
	return KindHTMLBlock
}

// NewHTMLBlock returns a new HTMLBlock node.
func NewHTMLBlock(typ HTMLBlockType) *HTMLBlock {
	return &HTMLBlock{
		BaseBlock:     BaseBlock{},
		HTMLBlockType: typ,
		ClosureLine:   textm.NewSegment(-1, -1),
	}
}
<commit_msg>Add AddMeta<commit_after>package ast

import (
	"fmt"
	"strings"

	textm "github.com/yuin/goldmark/text"
)

// A BaseBlock struct implements the Node interface partialliy.
type BaseBlock struct {
	BaseNode
	blankPreviousLines bool
	lines              *textm.Segments
}

// Type implements Node.Type
func (b *BaseBlock) Type() NodeType {
	return TypeBlock
}

// IsRaw implements Node.IsRaw
func (b *BaseBlock) IsRaw() bool {
	return false
}

// HasBlankPreviousLines implements Node.HasBlankPreviousLines.
func (b *BaseBlock) HasBlankPreviousLines() bool {
	return b.blankPreviousLines
}

// SetBlankPreviousLines implements Node.SetBlankPreviousLines.
func (b *BaseBlock) SetBlankPreviousLines(v bool) {
	b.blankPreviousLines = v
}

// Lines implements Node.Lines
func (b *BaseBlock) Lines() *textm.Segments {
	if b.lines == nil {
		b.lines = textm.NewSegments()
	}
	return b.lines
}

// SetLines implements Node.SetLines
func (b *BaseBlock) SetLines(v *textm.Segments) {
	b.lines = v
}

// A Document struct is a root node of Markdown text.
type Document struct {
	BaseBlock

	meta map[string]interface{}
}

// KindDocument is a NodeKind of the Document node.
var KindDocument = NewNodeKind("Document")

// Dump implements Node.Dump .
func (n *Document) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// Type implements Node.Type .
func (n *Document) Type() NodeType {
	return TypeDocument
}

// Kind implements Node.Kind.
func (n *Document) Kind() NodeKind {
	return KindDocument
}

// OwnerDocument implements Node.OwnerDocument
func (n *Document) OwnerDocument() *Document {
	return n
}

// Meta returns metadata of this document.
func (n *Document) Meta() map[string]interface{} {
	if n.meta == nil {
		n.meta = map[string]interface{}{}
	}
	return n.meta
}

// SetMeta sets given metadata to this document.
func (n *Document) SetMeta(meta map[string]interface{}) {
	if n.meta == nil {
		n.meta = map[string]interface{}{}
	}
	for k, v := range meta {
		n.meta[k] = v
	}
}

// AddMeta adds given metadata to this document.
func (n *Document) AddMeta(key string, value interface{}) {
	if n.meta == nil {
		n.meta = map[string]interface{}{}
	}
	n.meta[key] = value
}

// NewDocument returns a new Document node.
func NewDocument() *Document {
	return &Document{
		BaseBlock: BaseBlock{},
		meta:      nil,
	}
}

// A TextBlock struct is a node whose lines
// should be rendered without any containers.
type TextBlock struct {
	BaseBlock
}

// Dump implements Node.Dump .
func (n *TextBlock) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindTextBlock is a NodeKind of the TextBlock node.
var KindTextBlock = NewNodeKind("TextBlock")

// Kind implements Node.Kind.
func (n *TextBlock) Kind() NodeKind {
	return KindTextBlock
}

// NewTextBlock returns a new TextBlock node.
func NewTextBlock() *TextBlock {
	return &TextBlock{
		BaseBlock: BaseBlock{},
	}
}

// A Paragraph struct represents a paragraph of Markdown text.
type Paragraph struct {
	BaseBlock
}

// Dump implements Node.Dump .
func (n *Paragraph) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindParagraph is a NodeKind of the Paragraph node.
var KindParagraph = NewNodeKind("Paragraph")

// Kind implements Node.Kind.
func (n *Paragraph) Kind() NodeKind {
	return KindParagraph
}

// NewParagraph returns a new Paragraph node.
func NewParagraph() *Paragraph {
	return &Paragraph{
		BaseBlock: BaseBlock{},
	}
}

// IsParagraph returns true if the given node implements the Paragraph interface,
// otherwise false.
func IsParagraph(node Node) bool {
	_, ok := node.(*Paragraph)
	return ok
}

// A Heading struct represents headings like SetextHeading and ATXHeading.
type Heading struct {
	BaseBlock
	// Level returns a level of this heading.
	// This value is between 1 and 6.
	Level int
}

// Dump implements Node.Dump .
func (n *Heading) Dump(source []byte, level int) {
	m := map[string]string{
		"Level": fmt.Sprintf("%d", n.Level),
	}
	DumpHelper(n, source, level, m, nil)
}

// KindHeading is a NodeKind of the Heading node.
var KindHeading = NewNodeKind("Heading")

// Kind implements Node.Kind.
func (n *Heading) Kind() NodeKind {
	return KindHeading
}

// NewHeading returns a new Heading node.
func NewHeading(level int) *Heading {
	return &Heading{
		BaseBlock: BaseBlock{},
		Level:     level,
	}
}

// A ThematicBreak struct represents a thematic break of Markdown text.
type ThematicBreak struct {
	BaseBlock
}

// Dump implements Node.Dump .
func (n *ThematicBreak) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindThematicBreak is a NodeKind of the ThematicBreak node.
var KindThematicBreak = NewNodeKind("ThematicBreak")

// Kind implements Node.Kind.
func (n *ThematicBreak) Kind() NodeKind {
	return KindThematicBreak
}

// NewThematicBreak returns a new ThematicBreak node.
func NewThematicBreak() *ThematicBreak {
	return &ThematicBreak{
		BaseBlock: BaseBlock{},
	}
}

// A CodeBlock interface represents an indented code block of Markdown text.
type CodeBlock struct {
	BaseBlock
}

// IsRaw implements Node.IsRaw.
func (n *CodeBlock) IsRaw() bool {
	return true
}

// Dump implements Node.Dump .
func (n *CodeBlock) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindCodeBlock is a NodeKind of the CodeBlock node.
var KindCodeBlock = NewNodeKind("CodeBlock")

// Kind implements Node.Kind.
func (n *CodeBlock) Kind() NodeKind {
	return KindCodeBlock
}

// NewCodeBlock returns a new CodeBlock node.
func NewCodeBlock() *CodeBlock {
	return &CodeBlock{
		BaseBlock: BaseBlock{},
	}
}

// A FencedCodeBlock struct represents a fenced code block of Markdown text.
type FencedCodeBlock struct {
	BaseBlock
	// Info returns a info text of this fenced code block.
	Info *Text

	language []byte
}

// Language returns an language in an info string.
// Language returns nil if this node does not have an info string.
func (n *FencedCodeBlock) Language(source []byte) []byte {
	if n.language == nil && n.Info != nil {
		segment := n.Info.Segment
		info := segment.Value(source)
		i := 0
		for ; i < len(info); i++ {
			if info[i] == ' ' {
				break
			}
		}
		n.language = info[:i]
	}
	return n.language
}

// IsRaw implements Node.IsRaw.
func (n *FencedCodeBlock) IsRaw() bool {
	return true
}

// Dump implements Node.Dump .
func (n *FencedCodeBlock) Dump(source []byte, level int) {
	m := map[string]string{}
	if n.Info != nil {
		m["Info"] = fmt.Sprintf("\"%s\"", n.Info.Text(source))
	}
	DumpHelper(n, source, level, m, nil)
}

// KindFencedCodeBlock is a NodeKind of the FencedCodeBlock node.
var KindFencedCodeBlock = NewNodeKind("FencedCodeBlock")

// Kind implements Node.Kind.
func (n *FencedCodeBlock) Kind() NodeKind {
	return KindFencedCodeBlock
}

// NewFencedCodeBlock return a new FencedCodeBlock node.
func NewFencedCodeBlock(info *Text) *FencedCodeBlock {
	return &FencedCodeBlock{
		BaseBlock: BaseBlock{},
		Info:      info,
	}
}

// A Blockquote struct represents an blockquote block of Markdown text.
type Blockquote struct {
	BaseBlock
}

// Dump implements Node.Dump .
func (n *Blockquote) Dump(source []byte, level int) {
	DumpHelper(n, source, level, nil, nil)
}

// KindBlockquote is a NodeKind of the Blockquote node.
var KindBlockquote = NewNodeKind("Blockquote")

// Kind implements Node.Kind.
func (n *Blockquote) Kind() NodeKind {
	return KindBlockquote
}

// NewBlockquote returns a new Blockquote node.
func NewBlockquote() *Blockquote {
	return &Blockquote{
		BaseBlock: BaseBlock{},
	}
}

// A List struct represents a list of Markdown text.
type List struct {
	BaseBlock

	// Marker is a marker character like '-', '+', ')' and '.'.
	Marker byte

	// IsTight is a true if this list is a 'tight' list.
	// See https://spec.commonmark.org/0.30/#loose for details.
	IsTight bool

	// Start is an initial number of this ordered list.
	// If this list is not an ordered list, Start is 0.
	Start int
}

// IsOrdered returns true if this list is an ordered list, otherwise false.
func (l *List) IsOrdered() bool {
	return l.Marker == '.' || l.Marker == ')'
}

// CanContinue returns true if this list can continue with
// the given mark and a list type, otherwise false.
func (l *List) CanContinue(marker byte, isOrdered bool) bool {
	return marker == l.Marker && isOrdered == l.IsOrdered()
}

// Dump implements Node.Dump.
func (l *List) Dump(source []byte, level int) {
	m := map[string]string{
		"Ordered": fmt.Sprintf("%v", l.IsOrdered()),
		"Marker":  fmt.Sprintf("%c", l.Marker),
		"Tight":   fmt.Sprintf("%v", l.IsTight),
	}
	if l.IsOrdered() {
		m["Start"] = fmt.Sprintf("%d", l.Start)
	}
	DumpHelper(l, source, level, m, nil)
}

// KindList is a NodeKind of the List node.
var KindList = NewNodeKind("List")

// Kind implements Node.Kind.
func (l *List) Kind() NodeKind {
	return KindList
}

// NewList returns a new List node.
func NewList(marker byte) *List {
	return &List{
		BaseBlock: BaseBlock{},
		Marker:    marker,
		IsTight:   true,
	}
}

// A ListItem struct represents a list item of Markdown text.
type ListItem struct {
	BaseBlock

	// Offset is an offset position of this item.
	Offset int
}

// Dump implements Node.Dump.
func (n *ListItem) Dump(source []byte, level int) {
	m := map[string]string{
		"Offset": fmt.Sprintf("%d", n.Offset),
	}
	DumpHelper(n, source, level, m, nil)
}

// KindListItem is a NodeKind of the ListItem node.
var KindListItem = NewNodeKind("ListItem")

// Kind implements Node.Kind.
func (n *ListItem) Kind() NodeKind {
	return KindListItem
}

// NewListItem returns a new ListItem node.
func NewListItem(offset int) *ListItem {
	return &ListItem{
		BaseBlock: BaseBlock{},
		Offset:    offset,
	}
}

// HTMLBlockType represents kinds of an html blocks.
// See https://spec.commonmark.org/0.30/#html-blocks
type HTMLBlockType int

const (
	// HTMLBlockType1 represents type 1 html blocks
	HTMLBlockType1 HTMLBlockType = iota + 1
	// HTMLBlockType2 represents type 2 html blocks
	HTMLBlockType2
	// HTMLBlockType3 represents type 3 html blocks
	HTMLBlockType3
	// HTMLBlockType4 represents type 4 html blocks
	HTMLBlockType4
	// HTMLBlockType5 represents type 5 html blocks
	HTMLBlockType5
	// HTMLBlockType6 represents type 6 html blocks
	HTMLBlockType6
	// HTMLBlockType7 represents type 7 html blocks
	HTMLBlockType7
)

// An HTMLBlock struct represents an html block of Markdown text.
type HTMLBlock struct {
	BaseBlock

	// Type is a type of this html block.
	HTMLBlockType HTMLBlockType

	// ClosureLine is a line that closes this html block.
	ClosureLine textm.Segment
}

// IsRaw implements Node.IsRaw.
func (n *HTMLBlock) IsRaw() bool {
	return true
}

// HasClosure returns true if this html block has a closure line,
// otherwise false.
func (n *HTMLBlock) HasClosure() bool {
	return n.ClosureLine.Start >= 0
}

// Dump implements Node.Dump.
func (n *HTMLBlock) Dump(source []byte, level int) {
	indent := strings.Repeat("    ", level)
	fmt.Printf("%s%s {\n", indent, "HTMLBlock")
	indent2 := strings.Repeat("    ", level+1)
	fmt.Printf("%sRawText: \"", indent2)
	for i := 0; i < n.Lines().Len(); i++ {
		s := n.Lines().At(i)
		fmt.Print(string(source[s.Start:s.Stop]))
	}
	fmt.Printf("\"\n")
	for c := n.FirstChild(); c != nil; c = c.NextSibling() {
		c.Dump(source, level+1)
	}
	if n.HasClosure() {
		cl := n.ClosureLine
		fmt.Printf("%sClosure: \"%s\"\n", indent2, string(cl.Value(source)))
	}
	fmt.Printf("%s}\n", indent)
}

// KindHTMLBlock is a NodeKind of the HTMLBlock node.
var KindHTMLBlock = NewNodeKind("HTMLBlock")

// Kind implements Node.Kind.
func (n *HTMLBlock) Kind() NodeKind {
	return KindHTMLBlock
}

// NewHTMLBlock returns a new HTMLBlock node.
func NewHTMLBlock(typ HTMLBlockType) *HTMLBlock {
	return &HTMLBlock{
		BaseBlock:     BaseBlock{},
		HTMLBlockType: typ,
		ClosureLine:   textm.NewSegment(-1, -1),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/camptocamp/conplicity/engines"
	"github.com/camptocamp/conplicity/handler"
	"github.com/camptocamp/conplicity/providers"
	"github.com/camptocamp/conplicity/util"
	"github.com/camptocamp/conplicity/volume"
)

var version = "undefined"

func main() {
	var err error
	var exitCode int

	c, err := handler.NewConplicity(version)
	util.CheckErr(err, "Failed to setup Conplicity handler: %v", "fatal")

	log.Infof("Conplicity v%s starting backup...", version)

	vols, err := c.GetVolumes()
	util.CheckErr(err, "Failed to get Docker volumes: %v", "fatal")

	err = c.MetricsHandler.GetMetrics()
	util.CheckErr(err, "Failed to get exiting Prometheus metrics: %v", "fatal")

	for _, vol := range vols {
		c.LogTime(vol, "backupStartTime")
		err = backupVolume(c, vol)
		c.LogTime(vol, "backupEndTime")
		if err != nil {
			log.Errorf("Failed to backup volume %s: %v", vol.Name, err)
			exitCode = 1
			continue
		}
	}

	err = c.MetricsHandler.Push()
	if err != nil {
		log.Errorf("Failed to post data to Prometheus Pushgateway: %v", err)
		exitCode = 2
	}

	log.Infof("End backup...")
	os.Exit(exitCode)
}

func backupVolume(c *handler.Conplicity, vol *volume.Volume) (err error) {
	p := providers.GetProvider(c, vol)
	log.WithFields(log.Fields{
		"volume":   vol.Name,
		"provider": p.GetName(),
	}).Info("Found data provider")
	err = providers.PrepareBackup(p)
	if err != nil {
		err = fmt.Errorf("failed to prepare backup: %v", err)
		return
	}

	e := engines.GetEngine(c, vol)
	log.WithFields(log.Fields{
		"volume": vol.Name,
		"engine": e.GetName(),
	}).Info("Found backup engine")

	err = e.Backup()
	if err != nil {
		err = fmt.Errorf("failed to backup volume: %v", err)
		return
	}
	return
}
<commit_msg>Log typo<commit_after>package main

import (
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/camptocamp/conplicity/engines"
	"github.com/camptocamp/conplicity/handler"
	"github.com/camptocamp/conplicity/providers"
	"github.com/camptocamp/conplicity/util"
	"github.com/camptocamp/conplicity/volume"
)

var version = "undefined"

func main() {
	var err error
	var exitCode int

	c, err := handler.NewConplicity(version)
	util.CheckErr(err, "Failed to setup Conplicity handler: %v", "fatal")

	log.Infof("Conplicity v%s starting backup...", version)

	vols, err := c.GetVolumes()
	util.CheckErr(err, "Failed to get Docker volumes: %v", "fatal")

	err = c.MetricsHandler.GetMetrics()
	util.CheckErr(err, "Failed to get existing Prometheus metrics: %v", "fatal")

	for _, vol := range vols {
		c.LogTime(vol, "backupStartTime")
		err = backupVolume(c, vol)
		c.LogTime(vol, "backupEndTime")
		if err != nil {
			log.Errorf("Failed to backup volume %s: %v", vol.Name, err)
			exitCode = 1
			continue
		}
	}

	err = c.MetricsHandler.Push()
	if err != nil {
		log.Errorf("Failed to post data to Prometheus Pushgateway: %v", err)
		exitCode = 2
	}

	log.Infof("End backup...")
	os.Exit(exitCode)
}

func backupVolume(c *handler.Conplicity, vol *volume.Volume) (err error) {
	p := providers.GetProvider(c, vol)
	log.WithFields(log.Fields{
		"volume":   vol.Name,
		"provider": p.GetName(),
	}).Info("Found data provider")
	err = providers.PrepareBackup(p)
	if err != nil {
		err = fmt.Errorf("failed to prepare backup: %v", err)
		return
	}

	e := engines.GetEngine(c, vol)
	log.WithFields(log.Fields{
		"volume": vol.Name,
		"engine": e.GetName(),
	}).Info("Found backup engine")

	err = e.Backup()
	if err != nil {
		err = fmt.Errorf("failed to backup volume: %v", err)
		return
	}
	return
}
<|endoftext|>


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

package json

import (
	"bytes"
	"errors"
	"io"
)

// A Decoder reads and decodes JSON objects from an input stream.
type Decoder struct {
	r    io.Reader
	buf  []byte
	d    decodeState
	scan scanner
	err  error
}

// NewDecoder returns a new decoder that reads from r.
//
// The decoder introduces its own buffering and may
// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: r}
}

// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64.
func (dec *Decoder) UseNumber() { dec.d.useNumber = true }

// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
//
// See the documentation for Unmarshal for details about
// the conversion of JSON into a Go value.
func (dec *Decoder) Decode(v interface{}) error {
	if dec.err != nil {
		return dec.err
	}

	n, err := dec.readValue()
	if err != nil {
		return err
	}

	// Don't save err from unmarshal into dec.err:
	// the connection is still usable since we read a complete JSON
	// object from it before the error happened.
	dec.d.init(dec.buf[0:n])
	err = dec.d.unmarshal(v)

	// Slide rest of data down.
	rest := copy(dec.buf, dec.buf[n:])
	dec.buf = dec.buf[0:rest]

	return err
}

// Buffered returns a reader of the data remaining in the Decoder's
// buffer. The reader is valid until the next call to Decode.
func (dec *Decoder) Buffered() io.Reader {
	return bytes.NewReader(dec.buf)
}

// readValue reads a JSON value into dec.buf.
// It returns the length of the encoding.
func (dec *Decoder) readValue() (int, error) {
	dec.scan.reset()

	scanp := 0
	var err error
Input:
	for {
		// Look in the buffer for a new value.
		for i, c := range dec.buf[scanp:] {
			dec.scan.bytes++
			v := dec.scan.step(&dec.scan, int(c))
			if v == scanEnd {
				scanp += i
				break Input
			}
			// scanEnd is delayed one byte.
			// We might block trying to get that byte from src,
			// so instead invent a space byte.
			if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd {
				scanp += i + 1
				break Input
			}
			if v == scanError {
				dec.err = dec.scan.err
				return 0, dec.scan.err
			}
		}
		scanp = len(dec.buf)

		// Did the last read have an error?
		// Delayed until now to allow buffer scan.
		if err != nil {
			if err == io.EOF {
				if dec.scan.step(&dec.scan, ' ') == scanEnd {
					break Input
				}
				if nonSpace(dec.buf) {
					err = io.ErrUnexpectedEOF
				}
			}
			dec.err = err
			return 0, err
		}

		// Make room to read more into the buffer.
		const minRead = 512
		if cap(dec.buf)-len(dec.buf) < minRead {
			newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead)
			copy(newBuf, dec.buf)
			dec.buf = newBuf
		}

		// Read.  Delay error for next iteration (after scan).
		var n int
		n, err = dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)])
		dec.buf = dec.buf[0 : len(dec.buf)+n]
	}
	return scanp, nil
}

func nonSpace(b []byte) bool {
	for _, c := range b {
		if !isSpace(rune(c)) {
			return true
		}
	}
	return false
}

// An Encoder writes JSON objects to an output stream.
type Encoder struct {
	w   io.Writer
	e   encodeState
	err error
}

// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w: w}
}

// Encode writes the JSON encoding of v to the stream,
// followed by a newline character.
//
// See the documentation for Marshal for details about the
// conversion of Go values to JSON.
func (enc *Encoder) Encode(v interface{}) error {
	if enc.err != nil {
		return enc.err
	}
	e := newEncodeState()
	err := e.marshal(v)
	if err != nil {
		return err
	}

	// Terminate each value with a newline.
	// This makes the output look a little nicer
	// when debugging, and some kind of space
	// is required if the encoded value was a number,
	// so that the reader knows there aren't more
	// digits coming.
	e.WriteByte('\n')

	if _, err = enc.w.Write(e.Bytes()); err != nil {
		enc.err = err
	}
	encodeStatePool.Put(e)
	return err
}

// RawMessage is a raw encoded JSON object.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte

// MarshalJSON returns *m as the JSON encoding of m.
func (m *RawMessage) MarshalJSON() ([]byte, error) {
	return *m, nil
}

// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
	if m == nil {
		return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
	}
	*m = append((*m)[0:0], data...)
	return nil
}

var _ Marshaler = (*RawMessage)(nil)
var _ Unmarshaler = (*RawMessage)(nil)
<commit_msg>encoding/json: remove unused field from Encoder struct<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package json

import (
	"bytes"
	"errors"
	"io"
)

// A Decoder reads and decodes JSON objects from an input stream.
type Decoder struct {
	r    io.Reader
	buf  []byte
	d    decodeState
	scan scanner
	err  error
}

// NewDecoder returns a new decoder that reads from r.
//
// The decoder introduces its own buffering and may
// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: r}
}

// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64.
func (dec *Decoder) UseNumber() { dec.d.useNumber = true }

// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
//
// See the documentation for Unmarshal for details about
// the conversion of JSON into a Go value.
func (dec *Decoder) Decode(v interface{}) error {
	if dec.err != nil {
		return dec.err
	}

	n, err := dec.readValue()
	if err != nil {
		return err
	}

	// Don't save err from unmarshal into dec.err:
	// the connection is still usable since we read a complete JSON
	// object from it before the error happened.
	dec.d.init(dec.buf[0:n])
	err = dec.d.unmarshal(v)

	// Slide rest of data down.
	rest := copy(dec.buf, dec.buf[n:])
	dec.buf = dec.buf[0:rest]

	return err
}

// Buffered returns a reader of the data remaining in the Decoder's
// buffer. The reader is valid until the next call to Decode.
func (dec *Decoder) Buffered() io.Reader {
	return bytes.NewReader(dec.buf)
}

// readValue reads a JSON value into dec.buf.
// It returns the length of the encoding.
func (dec *Decoder) readValue() (int, error) {
	dec.scan.reset()

	scanp := 0
	var err error
Input:
	for {
		// Look in the buffer for a new value.
		for i, c := range dec.buf[scanp:] {
			dec.scan.bytes++
			v := dec.scan.step(&dec.scan, int(c))
			if v == scanEnd {
				scanp += i
				break Input
			}
			// scanEnd is delayed one byte.
			// We might block trying to get that byte from src,
			// so instead invent a space byte.
			if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd {
				scanp += i + 1
				break Input
			}
			if v == scanError {
				dec.err = dec.scan.err
				return 0, dec.scan.err
			}
		}
		scanp = len(dec.buf)

		// Did the last read have an error?
		// Delayed until now to allow buffer scan.
		if err != nil {
			if err == io.EOF {
				if dec.scan.step(&dec.scan, ' ') == scanEnd {
					break Input
				}
				if nonSpace(dec.buf) {
					err = io.ErrUnexpectedEOF
				}
			}
			dec.err = err
			return 0, err
		}

		// Make room to read more into the buffer.
		const minRead = 512
		if cap(dec.buf)-len(dec.buf) < minRead {
			newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead)
			copy(newBuf, dec.buf)
			dec.buf = newBuf
		}

		// Read.  Delay error for next iteration (after scan).
		var n int
		n, err = dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)])
		dec.buf = dec.buf[0 : len(dec.buf)+n]
	}
	return scanp, nil
}

func nonSpace(b []byte) bool {
	for _, c := range b {
		if !isSpace(rune(c)) {
			return true
		}
	}
	return false
}

// An Encoder writes JSON objects to an output stream.
type Encoder struct {
	w   io.Writer
	err error
}

// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w: w}
}

// Encode writes the JSON encoding of v to the stream,
// followed by a newline character.
//
// See the documentation for Marshal for details about the
// conversion of Go values to JSON.
func (enc *Encoder) Encode(v interface{}) error {
	if enc.err != nil {
		return enc.err
	}
	e := newEncodeState()
	err := e.marshal(v)
	if err != nil {
		return err
	}

	// Terminate each value with a newline.
	// This makes the output look a little nicer
	// when debugging, and some kind of space
	// is required if the encoded value was a number,
	// so that the reader knows there aren't more
	// digits coming.
	e.WriteByte('\n')

	if _, err = enc.w.Write(e.Bytes()); err != nil {
		enc.err = err
	}
	encodeStatePool.Put(e)
	return err
}

// RawMessage is a raw encoded JSON object.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte

// MarshalJSON returns *m as the JSON encoding of m.
func (m *RawMessage) MarshalJSON() ([]byte, error) {
	return *m, nil
}

// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
	if m == nil {
		return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
	}
	*m = append((*m)[0:0], data...)
	return nil
}

var _ Marshaler = (*RawMessage)(nil)
var _ Unmarshaler = (*RawMessage)(nil)
<|endoftext|>


<commit_before>package commands

import (
	"database/sql"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/camd67/moebot/moebot_bot/bot/permissions"
	"github.com/camd67/moebot/moebot_bot/util"

	"github.com/camd67/moebot/moebot_bot/util/db"
)

type ProfileCommand struct {
	MasterId string
}

func (pc *ProfileCommand) Execute(pack *CommPackage) {
	// special stuff for master rank
	if pc.MasterId == pack.message.Author.ID && len(pack.params) > 1 && pack.params[0] != "-a" {
		pack.session.ChannelMessageSend(pack.message.ChannelID, pack.message.Author.Mention()+"'s profile:\nMy favorite user! ❤")
		return
	}

	// technically we'll already have a user + server at this point, but may not have a usr. Still create if necessary
	server, err := db.ServerQueryOrInsert(pack.guild.ID)
	if err != nil {
		pack.session.ChannelMessageSend(pack.channel.ID, "Sorry, there was an issue fetching the server. This is an issue with moebot and not Discord.")
		return
	}
	_, err = db.UserQueryOrInsert(pack.message.Author.ID)
	if err != nil {
		pack.session.ChannelMessageSend(pack.channel.ID, "Sorry, there was an issue fetching your user. This is an issue with moebot and not Discord.")
		return
	}
	usr, err := db.UserServerRankQuery(pack.message.Author.ID, pack.guild.ID)
	if err != nil {
		if err != sql.ErrNoRows {
			pack.session.ChannelMessageSend(pack.message.ChannelID, "Sorry, there was an issue getting your information!")
			return
		} else {
			// ErrNoRows. Overwrite the usr value, so we don't accidentally get an NPE later
			// We don't want to bail out here since this just means the user doesn't exist
			usr = nil
		}
	}
	var message strings.Builder
	message.WriteString(pack.message.Author.Mention())
	message.WriteString("'s profile:")
	message.WriteString("\nRank: ")
	if usr != nil {
		message.WriteString(convertRankToString(usr.Rank, server.VeteranRank))
	} else {
		message.WriteString("Unranked")
	}
	message.WriteString("\nPermission Level: ")
	message.WriteString(util.MakeStringCode(pc.getPermissionLevel(pack)))
	message.WriteString("\nServer join date: ")
	t, err := time.Parse(time.RFC3339Nano, pack.member.JoinedAt)
	if err != nil {
		message.WriteString(util.MakeStringCode("Unknown"))
	} else {
		message.WriteString(util.MakeStringCode(t.Format(time.UnixDate)))
	}
	pack.session.ChannelMessageSend(pack.message.ChannelID, message.String())
}

func (pc *ProfileCommand) getPermissionLevel(pack *CommPackage) string {
	// special checks for certain roles that aren't in the database
	if pack.message.Author.ID == pc.MasterId {
		return db.SprintPermission(db.PermMaster)
	} else if permissions.IsGuildOwner(pack.guild, pack.message.Author.ID) {
		return db.SprintPermission(db.PermGuildOwner)
	}

	perms := db.RoleQueryPermission(pack.member.Roles)
	highestPerm := db.PermAll
	// Find the highest permission level this user has
	for _, userPerm := range perms {
		if userPerm > highestPerm {
			highestPerm = userPerm
		}
	}
	return db.GetPermissionString(highestPerm)
}

func convertRankToString(rank int, serverMax sql.NullInt64) (rankString string) {
	if !serverMax.Valid {
		// no server max? just give back the rank itself
		return strconv.Itoa(rank)
	}
	// naming strategy: every 1% till 10%, then every 2% until 30%, then every 3% until 60% then every 4% until 100%, then every 100% forever
	rankPrefixes := []string{"Newcomer", "Apprentice", "Rookie", "Regular", "Veteran"}
	rankSeparator := " --> "
	percent := float64(rank) / float64(serverMax.Int64) * 100.0
	var rankPrefixIndex int
	var rankSuffix int
	if percent < 10 {
		rankSuffix = int(percent / 2.0)
		rankPrefixIndex = 0
	} else if percent < 30 {
		rankSuffix = int((percent - 10) / 4.0)
		rankPrefixIndex = 1
	} else if percent < 60 {
		rankSuffix = int((percent - 30) / 6.0)
		rankPrefixIndex = 2
	} else if percent < 100 {
		rankSuffix = int((percent - 60) / 8.0)
		rankPrefixIndex = 3
	} else {
		rankSuffix = int((percent - 100) / 100)
		rankPrefixIndex = 4
	}
	if rankSuffix != 0 {
		rankPrefixes[rankPrefixIndex] = util.MakeStringBold(rankPrefixes[rankPrefixIndex] + " " + strconv.Itoa(rankSuffix))
	} else {
		rankPrefixes[rankPrefixIndex] = util.MakeStringBold(rankPrefixes[rankPrefixIndex])
	}
	return convertToEmphasizedRankString(rankPrefixes, rankPrefixIndex, rankSeparator)
}

/*
Converts an array of strings to an emphasized string, currently used only for ranks. Looks like:
~element1~,**element2**, element3, element4
*/
func convertToEmphasizedRankString(ranks []string, indexToApply int, sep string) string {
	var s string
	for index, rankString := range ranks {
		if index < indexToApply {
			s += util.MakeStringStrikethrough(rankString)
		} else {
			s += rankString
		}
		if index < len(ranks)-1 {
			s += sep
		}
	}
	return s
}

func (pc *ProfileCommand) GetPermLevel() db.Permission {
	return db.PermAll
}

func (pc *ProfileCommand) GetCommandKeys() []string {
	return []string{"PROFILE"}
}

func (pc *ProfileCommand) GetCommandHelp(commPrefix string) string {
	return fmt.Sprintf("`%[1]s profile` - Displays your server profile", commPrefix)
}
<commit_msg>Fixes issue of not displaying master profile<commit_after>package commands

import (
	"database/sql"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/camd67/moebot/moebot_bot/bot/permissions"
	"github.com/camd67/moebot/moebot_bot/util"

	"github.com/camd67/moebot/moebot_bot/util/db"
)

type ProfileCommand struct {
	MasterId string
}

func (pc *ProfileCommand) Execute(pack *CommPackage) {
	// special stuff for master rank
	if pc.MasterId == pack.message.Author.ID || (len(pack.params) > 1 && pack.params[0] != "-a") {
		pack.session.ChannelMessageSend(pack.message.ChannelID, pack.message.Author.Mention()+"'s profile:\nMy favorite user! ❤")
		return
	}

	// technically we'll already have a user + server at this point, but may not have a usr. Still create if necessary
	server, err := db.ServerQueryOrInsert(pack.guild.ID)
	if err != nil {
		pack.session.ChannelMessageSend(pack.channel.ID, "Sorry, there was an issue fetching the server. This is an issue with moebot and not Discord.")
		return
	}
	_, err = db.UserQueryOrInsert(pack.message.Author.ID)
	if err != nil {
		pack.session.ChannelMessageSend(pack.channel.ID, "Sorry, there was an issue fetching your user. This is an issue with moebot and not Discord.")
		return
	}
	usr, err := db.UserServerRankQuery(pack.message.Author.ID, pack.guild.ID)
	if err != nil {
		if err != sql.ErrNoRows {
			pack.session.ChannelMessageSend(pack.message.ChannelID, "Sorry, there was an issue getting your information!")
			return
		} else {
			// ErrNoRows. Overwrite the usr value, so we don't accidentally get an NPE later
			// We don't want to bail out here since this just means the user doesn't exist
			usr = nil
		}
	}
	var message strings.Builder
	message.WriteString(pack.message.Author.Mention())
	message.WriteString("'s profile:")
	message.WriteString("\nRank: ")
	if usr != nil {
		message.WriteString(convertRankToString(usr.Rank, server.VeteranRank))
	} else {
		message.WriteString("Unranked")
	}
	message.WriteString("\nPermission Level: ")
	message.WriteString(util.MakeStringCode(pc.getPermissionLevel(pack)))
	message.WriteString("\nServer join date: ")
	t, err := time.Parse(time.RFC3339Nano, pack.member.JoinedAt)
	if err != nil {
		message.WriteString(util.MakeStringCode("Unknown"))
	} else {
		message.WriteString(util.MakeStringCode(t.Format(time.UnixDate)))
	}
	pack.session.ChannelMessageSend(pack.message.ChannelID, message.String())
}

func (pc *ProfileCommand) getPermissionLevel(pack *CommPackage) string {
	// special checks for certain roles that aren't in the database
	if pack.message.Author.ID == pc.MasterId {
		return db.SprintPermission(db.PermMaster)
	} else if permissions.IsGuildOwner(pack.guild, pack.message.Author.ID) {
		return db.SprintPermission(db.PermGuildOwner)
	}

	perms := db.RoleQueryPermission(pack.member.Roles)
	highestPerm := db.PermAll
	// Find the highest permission level this user has
	for _, userPerm := range perms {
		if userPerm > highestPerm {
			highestPerm = userPerm
		}
	}
	return db.GetPermissionString(highestPerm)
}

func convertRankToString(rank int, serverMax sql.NullInt64) (rankString string) {
	if !serverMax.Valid {
		// no server max? just give back the rank itself
		return strconv.Itoa(rank)
	}
	// naming strategy: every 1% till 10%, then every 2% until 30%, then every 3% until 60% then every 4% until 100%, then every 100% forever
	rankPrefixes := []string{"Newcomer", "Apprentice", "Rookie", "Regular", "Veteran"}
	rankSeparator := " --> "
	percent := float64(rank) / float64(serverMax.Int64) * 100.0
	var rankPrefixIndex int
	var rankSuffix int
	if percent < 10 {
		rankSuffix = int(percent / 2.0)
		rankPrefixIndex = 0
	} else if percent < 30 {
		rankSuffix = int((percent - 10) / 4.0)
		rankPrefixIndex = 1
	} else if percent < 60 {
		rankSuffix = int((percent - 30) / 6.0)
		rankPrefixIndex = 2
	} else if percent < 100 {
		rankSuffix = int((percent - 60) / 8.0)
		rankPrefixIndex = 3
	} else {
		rankSuffix = int((percent - 100) / 100)
		rankPrefixIndex = 4
	}
	if rankSuffix != 0 {
		rankPrefixes[rankPrefixIndex] = util.MakeStringBold(rankPrefixes[rankPrefixIndex] + " " + strconv.Itoa(rankSuffix))
	} else {
		rankPrefixes[rankPrefixIndex] = util.MakeStringBold(rankPrefixes[rankPrefixIndex])
	}
	return convertToEmphasizedRankString(rankPrefixes, rankPrefixIndex, rankSeparator)
}

/*
Converts an array of strings to an emphasized string, currently used only for ranks. Looks like:
~element1~,**element2**, element3, element4
*/
func convertToEmphasizedRankString(ranks []string, indexToApply int, sep string) string {
	var s string
	for index, rankString := range ranks {
		if index < indexToApply {
			s += util.MakeStringStrikethrough(rankString)
		} else {
			s += rankString
		}
		if index < len(ranks)-1 {
			s += sep
		}
	}
	return s
}

func (pc *ProfileCommand) GetPermLevel() db.Permission {
	return db.PermAll
}

func (pc *ProfileCommand) GetCommandKeys() []string {
	return []string{"PROFILE"}
}

func (pc *ProfileCommand) GetCommandHelp(commPrefix string) string {
	return fmt.Sprintf("`%[1]s profile` - Displays your server profile", commPrefix)
}
<|endoftext|>


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

package net

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"syscall"
	"testing"
	"time"
)

func TestAcceptIgnoreSomeErrors(t *testing.T) {
	recv := func(ln Listener) (string, error) {
		c, err := ln.Accept()
		if err != nil {
			// Display windows errno in error message.
			operr, ok := err.(*OpError)
			if !ok {
				return "", err
			}
			errno, ok := operr.Err.(syscall.Errno)
			if !ok {
				return "", err
			}
			return "", fmt.Errorf("%v (windows errno=%d)", err, errno)
		}
		defer c.Close()

		b := make([]byte, 100)
		n, err := c.Read(b)
		if err != nil && err != io.EOF {
			return "", err
		}
		return string(b[:n]), nil
	}

	send := func(addr string, data string) error {
		c, err := Dial("tcp", addr)
		if err != nil {
			return err
		}
		defer c.Close()

		b := []byte(data)
		n, err := c.Write(b)
		if err != nil {
			return err
		}
		if n != len(b) {
			return fmt.Errorf(`Only %d chars of string "%s" sent`, n, data)
		}
		return nil
	}

	if envaddr := os.Getenv("GOTEST_DIAL_ADDR"); envaddr != "" {
		// In child process.
		c, err := Dial("tcp", envaddr)
		if err != nil {
			t.Fatalf("Dial failed: %v", err)
		}
		fmt.Printf("sleeping\n")
		time.Sleep(time.Minute) // process will be killed here
		c.Close()
	}

	ln, err := Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("Listen failed: %v", err)
	}
	defer ln.Close()

	// Start child process that connects to our listener.
	cmd := exec.Command(os.Args[0], "-test.run=TestAcceptIgnoreSomeErrors")
	cmd.Env = append(os.Environ(), "GOTEST_DIAL_ADDR="+ln.Addr().String())
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		t.Fatalf("cmd.StdoutPipe failed: %v", err)
	}
	err = cmd.Start()
	if err != nil {
		t.Fatalf("cmd.Start failed: %v\n%s\n", err)
	}
	outReader := bufio.NewReader(stdout)
	for {
		s, err := outReader.ReadString('\n')
		if err != nil {
			t.Fatalf("reading stdout failed: %v", err)
		}
		if s == "sleeping\n" {
			break
		}
	}
	defer cmd.Wait() // ignore error - we know it is getting killed

	const alittle = 100 * time.Millisecond
	time.Sleep(alittle)
	cmd.Process.Kill() // the only way to trigger the errors
	time.Sleep(alittle)

	// Send second connection data (with delay in a separate goroutine).
	result := make(chan error)
	go func() {
		time.Sleep(alittle)
		err := send(ln.Addr().String(), "abc")
		if err != nil {
			result <- err
		}
		result <- nil
	}()
	defer func() {
		err := <-result
		if err != nil {
			t.Fatalf("send failed: %v", err)
		}
	}()

	// Receive first or second connection.
	s, err := recv(ln)
	if err != nil {
		t.Fatalf("recv failed: %v", err)
	}
	switch s {
	case "":
		// First connection data is received, lets get second connection data.
	case "abc":
		// First connection is lost forever, but that is ok.
		return
	default:
		t.Fatalf(`"%s" received from recv, but "" or "abc" expected`, s)
	}

	// Get second connection data.
	s, err = recv(ln)
	if err != nil {
		t.Fatalf("recv failed: %v", err)
	}
	if s != "abc" {
		t.Fatalf(`"%s" received from recv, but "abc" expected`, s)
	}
}
<commit_msg>net: fix format string in TestAcceptIgnoreSomeErrors<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package net

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"syscall"
	"testing"
	"time"
)

func TestAcceptIgnoreSomeErrors(t *testing.T) {
	recv := func(ln Listener) (string, error) {
		c, err := ln.Accept()
		if err != nil {
			// Display windows errno in error message.
			operr, ok := err.(*OpError)
			if !ok {
				return "", err
			}
			errno, ok := operr.Err.(syscall.Errno)
			if !ok {
				return "", err
			}
			return "", fmt.Errorf("%v (windows errno=%d)", err, errno)
		}
		defer c.Close()

		b := make([]byte, 100)
		n, err := c.Read(b)
		if err != nil && err != io.EOF {
			return "", err
		}
		return string(b[:n]), nil
	}

	send := func(addr string, data string) error {
		c, err := Dial("tcp", addr)
		if err != nil {
			return err
		}
		defer c.Close()

		b := []byte(data)
		n, err := c.Write(b)
		if err != nil {
			return err
		}
		if n != len(b) {
			return fmt.Errorf(`Only %d chars of string "%s" sent`, n, data)
		}
		return nil
	}

	if envaddr := os.Getenv("GOTEST_DIAL_ADDR"); envaddr != "" {
		// In child process.
		c, err := Dial("tcp", envaddr)
		if err != nil {
			t.Fatalf("Dial failed: %v", err)
		}
		fmt.Printf("sleeping\n")
		time.Sleep(time.Minute) // process will be killed here
		c.Close()
	}

	ln, err := Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("Listen failed: %v", err)
	}
	defer ln.Close()

	// Start child process that connects to our listener.
	cmd := exec.Command(os.Args[0], "-test.run=TestAcceptIgnoreSomeErrors")
	cmd.Env = append(os.Environ(), "GOTEST_DIAL_ADDR="+ln.Addr().String())
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		t.Fatalf("cmd.StdoutPipe failed: %v", err)
	}
	err = cmd.Start()
	if err != nil {
		t.Fatalf("cmd.Start failed: %v\n", err)
	}
	outReader := bufio.NewReader(stdout)
	for {
		s, err := outReader.ReadString('\n')
		if err != nil {
			t.Fatalf("reading stdout failed: %v", err)
		}
		if s == "sleeping\n" {
			break
		}
	}
	defer cmd.Wait() // ignore error - we know it is getting killed

	const alittle = 100 * time.Millisecond
	time.Sleep(alittle)
	cmd.Process.Kill() // the only way to trigger the errors
	time.Sleep(alittle)

	// Send second connection data (with delay in a separate goroutine).
	result := make(chan error)
	go func() {
		time.Sleep(alittle)
		err := send(ln.Addr().String(), "abc")
		if err != nil {
			result <- err
		}
		result <- nil
	}()
	defer func() {
		err := <-result
		if err != nil {
			t.Fatalf("send failed: %v", err)
		}
	}()

	// Receive first or second connection.
	s, err := recv(ln)
	if err != nil {
		t.Fatalf("recv failed: %v", err)
	}
	switch s {
	case "":
		// First connection data is received, lets get second connection data.
	case "abc":
		// First connection is lost forever, but that is ok.
		return
	default:
		t.Fatalf(`"%s" received from recv, but "" or "abc" expected`, s)
	}

	// Get second connection data.
	s, err = recv(ln)
	if err != nil {
		t.Fatalf("recv failed: %v", err)
	}
	if s != "abc" {
		t.Fatalf(`"%s" received from recv, but "abc" expected`, s)
	}
}
<|endoftext|>