<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package master

import (
	"encoding/json"
	"reflect"
	"strings"
	"testing"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/kubernetes/pkg/api"
)

func TestGroupVersions(t *testing.T) {
	// legacyUnsuffixedGroups contains the groups released prior to deciding that kubernetes API groups should be dns-suffixed
	// new groups should be suffixed with ".k8s.io" (https://github.com/kubernetes/kubernetes/pull/31887#issuecomment-244462396)
	legacyUnsuffixedGroups := sets.NewString(
		"",
		"apps",
		"autoscaling",
		"batch",
		"componentconfig",
		"extensions",
		"federation",
		"policy",
	)

	// No new groups should be added to the legacyUnsuffixedGroups exclusion list
	if len(legacyUnsuffixedGroups) != 8 {
		t.Errorf("No additional unnamespaced groups should be created")
	}

	for _, gv := range api.Registry.RegisteredGroupVersions() {
		if !strings.HasSuffix(gv.Group, ".k8s.io") && !legacyUnsuffixedGroups.Has(gv.Group) {
			t.Errorf("Group %s does not have the standard kubernetes API group suffix of .k8s.io", gv.Group)
		}
	}
}

func TestTypeTags(t *testing.T) {
	for gvk, knownType := range api.Scheme.AllKnownTypes() {
		if gvk.Version == runtime.APIVersionInternal {
			ensureNoTags(t, gvk, knownType, nil)
		} else {
			ensureTags(t, gvk, knownType, nil)
		}
	}
}

// These types are registered in external versions, and therefore include json tags,
// but are also registered in internal versions (or referenced from internal types),
// so we explicitly allow tags for them
var typesAllowedTags = map[reflect.Type]bool{
	reflect.TypeOf(intstr.IntOrString{}):          true,
	reflect.TypeOf(metav1.Time{}):                 true,
	reflect.TypeOf(metav1.Duration{}):             true,
	reflect.TypeOf(metav1.TypeMeta{}):             true,
	reflect.TypeOf(metav1.ListMeta{}):             true,
	reflect.TypeOf(metav1.ObjectMeta{}):           true,
	reflect.TypeOf(metav1.OwnerReference{}):       true,
	reflect.TypeOf(metav1.LabelSelector{}):        true,
	reflect.TypeOf(metav1.GetOptions{}):           true,
	reflect.TypeOf(metav1.ExportOptions{}):        true,
	reflect.TypeOf(metav1.ListOptions{}):          true,
	reflect.TypeOf(metav1.DeleteOptions{}):        true,
	reflect.TypeOf(metav1.GroupVersionKind{}):     true,
	reflect.TypeOf(metav1.GroupVersionResource{}): true,
	reflect.TypeOf(metav1.Status{}):               true,
}

func ensureNoTags(t *testing.T, gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type) {
	if _, ok := typesAllowedTags[tp]; ok {
		return
	}

	parents = append(parents, tp)

	switch tp.Kind() {
	case reflect.Map, reflect.Slice, reflect.Ptr:
		ensureNoTags(t, gvk, tp.Elem(), parents)

	case reflect.String, reflect.Bool, reflect.Float32, reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uintptr, reflect.Uint32, reflect.Uint64, reflect.Interface:
		// no-op

	case reflect.Struct:
		for i := 0; i < tp.NumField(); i++ {
			f := tp.Field(i)
			jsonTag := f.Tag.Get("json")
			protoTag := f.Tag.Get("protobuf")
			if len(jsonTag) > 0 || len(protoTag) > 0 {
				t.Errorf("Internal types should not have json or protobuf tags. %#v has tag on field %v: %v", gvk, f.Name, f.Tag)
				for i, tp := range parents {
					t.Logf("%s%v:", strings.Repeat("  ", i), tp)
				}
			}

			ensureNoTags(t, gvk, f.Type, parents)
		}

	default:
		t.Errorf("Unexpected type %v in %#v", tp.Kind(), gvk)
		for i, tp := range parents {
			t.Logf("%s%v:", strings.Repeat("  ", i), tp)
		}
	}
}

var (
	marshalerType   = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
	unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
)

// These fields are limited exceptions to the standard JSON naming structure.
// Additions should only be made if a non-standard field name was released and cannot be changed for compatibility reasons.
var allowedNonstandardJSONNames = map[reflect.Type]string{
	reflect.TypeOf(v1.DaemonEndpoint{}): "Port",
}

func ensureTags(t *testing.T, gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type) {
	// This type handles its own encoding/decoding and doesn't need json tags
	if tp.Implements(marshalerType) && (tp.Implements(unmarshalerType) || reflect.PtrTo(tp).Implements(unmarshalerType)) {
		return
	}

	parents = append(parents, tp)

	switch tp.Kind() {
	case reflect.Map, reflect.Slice, reflect.Ptr:
		ensureTags(t, gvk, tp.Elem(), parents)

	case reflect.String, reflect.Bool, reflect.Float32, reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uintptr, reflect.Uint32, reflect.Uint64, reflect.Interface:
		// no-op

	case reflect.Struct:
		for i := 0; i < tp.NumField(); i++ {
			f := tp.Field(i)
			jsonTag := f.Tag.Get("json")
			if len(jsonTag) == 0 {
				t.Errorf("External types should have json tags. %#v tags on field %v are: %s", gvk, f.Name, f.Tag)
				for i, tp := range parents {
					t.Logf("%s%v", strings.Repeat("  ", i), tp)
				}
			}

			jsonTagName := strings.Split(jsonTag, ",")[0]
			if len(jsonTagName) > 0 && (jsonTagName[0] < 'a' || jsonTagName[0] > 'z') && jsonTagName != "-" && allowedNonstandardJSONNames[tp] != jsonTagName {
				t.Errorf("External types should have json names starting with lowercase letter. %#v has json tag on field %v with name %s", gvk, f.Name, jsonTagName)
				t.Log(tp)
				t.Log(allowedNonstandardJSONNames[tp])
				for i, tp := range parents {
					t.Logf("%s%v", strings.Repeat("  ", i), tp)
				}
			}

			ensureTags(t, gvk, f.Type, parents)
		}

	default:
		t.Errorf("Unexpected type %v in %#v", tp.Kind(), gvk)
		for i, tp := range parents {
			t.Logf("%s%v:", strings.Repeat("  ", i), tp)
		}
	}
}
<commit_msg>Add metav1.MicroTime to exceptions in tag tests<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package master

import (
	"encoding/json"
	"reflect"
	"strings"
	"testing"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/kubernetes/pkg/api"
)

func TestGroupVersions(t *testing.T) {
	// legacyUnsuffixedGroups contains the groups released prior to deciding that kubernetes API groups should be dns-suffixed
	// new groups should be suffixed with ".k8s.io" (https://github.com/kubernetes/kubernetes/pull/31887#issuecomment-244462396)
	legacyUnsuffixedGroups := sets.NewString(
		"",
		"apps",
		"autoscaling",
		"batch",
		"componentconfig",
		"extensions",
		"federation",
		"policy",
	)

	// No new groups should be added to the legacyUnsuffixedGroups exclusion list
	if len(legacyUnsuffixedGroups) != 8 {
		t.Errorf("No additional unnamespaced groups should be created")
	}

	for _, gv := range api.Registry.RegisteredGroupVersions() {
		if !strings.HasSuffix(gv.Group, ".k8s.io") && !legacyUnsuffixedGroups.Has(gv.Group) {
			t.Errorf("Group %s does not have the standard kubernetes API group suffix of .k8s.io", gv.Group)
		}
	}
}

func TestTypeTags(t *testing.T) {
	for gvk, knownType := range api.Scheme.AllKnownTypes() {
		if gvk.Version == runtime.APIVersionInternal {
			ensureNoTags(t, gvk, knownType, nil)
		} else {
			ensureTags(t, gvk, knownType, nil)
		}
	}
}

// These types are registered in external versions, and therefore include json tags,
// but are also registered in internal versions (or referenced from internal types),
// so we explicitly allow tags for them
var typesAllowedTags = map[reflect.Type]bool{
	reflect.TypeOf(intstr.IntOrString{}):          true,
	reflect.TypeOf(metav1.Time{}):                 true,
	reflect.TypeOf(metav1.MicroTime{}):            true,
	reflect.TypeOf(metav1.Duration{}):             true,
	reflect.TypeOf(metav1.TypeMeta{}):             true,
	reflect.TypeOf(metav1.ListMeta{}):             true,
	reflect.TypeOf(metav1.ObjectMeta{}):           true,
	reflect.TypeOf(metav1.OwnerReference{}):       true,
	reflect.TypeOf(metav1.LabelSelector{}):        true,
	reflect.TypeOf(metav1.GetOptions{}):           true,
	reflect.TypeOf(metav1.ExportOptions{}):        true,
	reflect.TypeOf(metav1.ListOptions{}):          true,
	reflect.TypeOf(metav1.DeleteOptions{}):        true,
	reflect.TypeOf(metav1.GroupVersionKind{}):     true,
	reflect.TypeOf(metav1.GroupVersionResource{}): true,
	reflect.TypeOf(metav1.Status{}):               true,
}

func ensureNoTags(t *testing.T, gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type) {
	if _, ok := typesAllowedTags[tp]; ok {
		return
	}

	parents = append(parents, tp)

	switch tp.Kind() {
	case reflect.Map, reflect.Slice, reflect.Ptr:
		ensureNoTags(t, gvk, tp.Elem(), parents)

	case reflect.String, reflect.Bool, reflect.Float32, reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uintptr, reflect.Uint32, reflect.Uint64, reflect.Interface:
		// no-op

	case reflect.Struct:
		for i := 0; i < tp.NumField(); i++ {
			f := tp.Field(i)
			jsonTag := f.Tag.Get("json")
			protoTag := f.Tag.Get("protobuf")
			if len(jsonTag) > 0 || len(protoTag) > 0 {
				t.Errorf("Internal types should not have json or protobuf tags. %#v has tag on field %v: %v", gvk, f.Name, f.Tag)
				for i, tp := range parents {
					t.Logf("%s%v:", strings.Repeat("  ", i), tp)
				}
			}

			ensureNoTags(t, gvk, f.Type, parents)
		}

	default:
		t.Errorf("Unexpected type %v in %#v", tp.Kind(), gvk)
		for i, tp := range parents {
			t.Logf("%s%v:", strings.Repeat("  ", i), tp)
		}
	}
}

var (
	marshalerType   = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
	unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
)

// These fields are limited exceptions to the standard JSON naming structure.
// Additions should only be made if a non-standard field name was released and cannot be changed for compatibility reasons.
var allowedNonstandardJSONNames = map[reflect.Type]string{
	reflect.TypeOf(v1.DaemonEndpoint{}): "Port",
}

func ensureTags(t *testing.T, gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type) {
	// This type handles its own encoding/decoding and doesn't need json tags
	if tp.Implements(marshalerType) && (tp.Implements(unmarshalerType) || reflect.PtrTo(tp).Implements(unmarshalerType)) {
		return
	}

	parents = append(parents, tp)

	switch tp.Kind() {
	case reflect.Map, reflect.Slice, reflect.Ptr:
		ensureTags(t, gvk, tp.Elem(), parents)

	case reflect.String, reflect.Bool, reflect.Float32, reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uintptr, reflect.Uint32, reflect.Uint64, reflect.Interface:
		// no-op

	case reflect.Struct:
		for i := 0; i < tp.NumField(); i++ {
			f := tp.Field(i)
			jsonTag := f.Tag.Get("json")
			if len(jsonTag) == 0 {
				t.Errorf("External types should have json tags. %#v tags on field %v are: %s", gvk, f.Name, f.Tag)
				for i, tp := range parents {
					t.Logf("%s%v", strings.Repeat("  ", i), tp)
				}
			}

			jsonTagName := strings.Split(jsonTag, ",")[0]
			if len(jsonTagName) > 0 && (jsonTagName[0] < 'a' || jsonTagName[0] > 'z') && jsonTagName != "-" && allowedNonstandardJSONNames[tp] != jsonTagName {
				t.Errorf("External types should have json names starting with lowercase letter. %#v has json tag on field %v with name %s", gvk, f.Name, jsonTagName)
				t.Log(tp)
				t.Log(allowedNonstandardJSONNames[tp])
				for i, tp := range parents {
					t.Logf("%s%v", strings.Repeat("  ", i), tp)
				}
			}

			ensureTags(t, gvk, f.Type, parents)
		}

	default:
		t.Errorf("Unexpected type %v in %#v", tp.Kind(), gvk)
		for i, tp := range parents {
			t.Logf("%s%v:", strings.Repeat("  ", i), tp)
		}
	}
}
<|endoftext|>


<commit_before>package caixa

const responseCaixa = `
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <manutencaocobrancabancaria:SERVICO_SAIDA xmlns:manutencaocobrancabancaria="http://caixa.gov.br/sibar/manutencao_cobranca_bancaria/boleto/externo" xmlns:sibar_base="http://caixa.gov.br/sibar">
            <sibar_base:HEADER>
                <OPERACAO>{{operation}}</OPERACAO>
                <DATA_HORA>{{datetime}}</DATA_HORA>
            </sibar_base:HEADER>
            <DADOS>
                <CONTROLE_NEGOCIAL>
                    <ORIGEM_RETORNO>SIGCB</ORIGEM_RETORNO>
                    <COD_RETORNO>{{returnCode}}</COD_RETORNO>
                    <MENSAGENS>
                        <RETORNO>{{returnMessage}}</RETORNO>
                    </MENSAGENS>
                </CONTROLE_NEGOCIAL>
                <INCLUI_BOLETO>
                    <EXCECAO>{{exception}}</EXCECAO>
                    <CODIGO_BARRAS>{{barcodeNumber}}</CODIGO_BARRAS>
                    <LINHA_DIGITAVEL>{{digitableLine}}</LINHA_DIGITAVEL>
                    <NOSSO_NUMERO>{{ourNumber}}</NOSSO_NUMERO>
                    <URL>{{url}}</URL>
                </INCLUI_BOLETO>
            </DADOS>
        </manutencaocobrancabancaria:SERVICO_SAIDA>
    </soapenv:Body>
</soapenv:Envelope>
`

const incluiBoleto = `

## SOAPAction:IncluiBoleto
## Content-Type:text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ext="http://caixa.gov.br/sibar/manutencao_cobranca_bancaria/boleto/externo" xmlns:sib="http://caixa.gov.br/sibar">
<soapenv:Body>
<ext:SERVICO_ENTRADA >
         <sib:HEADER>
            <VERSAO>1.0</VERSAO>
            <AUTENTICACAO>{{unscape .Authentication.AuthorizationToken}}</AUTENTICACAO>
            <USUARIO_SERVICO>{{caixaEnv}}</USUARIO_SERVICO>
            <OPERACAO>INCLUI_BOLETO</OPERACAO>
            <SISTEMA_ORIGEM>SIGCB</SISTEMA_ORIGEM>
            <UNIDADE>{{.Agreement.Agency}}</UNIDADE>
            <DATA_HORA>{{fullDate today}}</DATA_HORA>
            </sib:HEADER>
         <DADOS>
            <INCLUI_BOLETO>
              <CODIGO_BENEFICIARIO>{{padLeft (toString .Agreement.AgreementNumber) "0" 7}}</CODIGO_BENEFICIARIO>
               <TITULO>
                  <NOSSO_NUMERO>{{toString .Title.OurNumber}}</NOSSO_NUMERO>
                  <NUMERO_DOCUMENTO>{{truncate .Title.DocumentNumber 11}}</NUMERO_DOCUMENTO>
                  <DATA_VENCIMENTO>{{enDate .Title.ExpireDateTime "-"}}</DATA_VENCIMENTO>
                  <VALOR>{{toFloatStr .Title.AmountInCents}}</VALOR>
                  <TIPO_ESPECIE>99</TIPO_ESPECIE>
                  <FLAG_ACEITE>S</FLAG_ACEITE>
                  <DATA_EMISSAO>{{enDate today "-"}}</DATA_EMISSAO>
                  <JUROS_MORA>
                     <TIPO>ISENTO</TIPO>
                     <VALOR>0</VALOR>
                  </JUROS_MORA>
                  <VALOR_ABATIMENTO>0</VALOR_ABATIMENTO>
                  <POS_VENCIMENTO>
                     <ACAO>DEVOLVER</ACAO>
                    <NUMERO_DIAS>0</NUMERO_DIAS>
                  </POS_VENCIMENTO>
                  <CODIGO_MOEDA>9</CODIGO_MOEDA>
                  <PAGADOR>
                     {{if eq .Buyer.Document.Type "CPF"}}
					 	<CPF>{{.Buyer.Document.Number}}</CPF>
                     	<NOME>{{truncate .Buyer.Name 40}}</NOME>
                     {{else}}
					 	<CNPJ>{{.Buyer.Document.Number}}</CNPJ>
                     	<RAZAO_SOCIAL>{{truncate .Buyer.Name 40}}</RAZAO_SOCIAL>
					 {{end}}
                     <ENDERECO>
                     <LOGRADOURO>{{truncateManyFields 40 .Buyer.Address.Street .Buyer.Address.Number .Buyer.Address.Complement}}</LOGRADOURO>
                        <BAIRRO>{{truncate .Buyer.Address.District 15}}</BAIRRO>
                        <CIDADE>{{truncate .Buyer.Address.City 15}}</CIDADE>
                        <UF>{{truncate .Buyer.Address.StateCode 2}}</UF>
                        <CEP>{{truncate .Buyer.Address.ZipCode 8}}</CEP>
                     </ENDERECO>
                  </PAGADOR>
                  <FICHA_COMPENSACAO>
                     <MENSAGENS>
                        <MENSAGEM>{{clearString (truncate .Title.Instructions 40)}}</MENSAGEM>
                        </MENSAGENS>
                  </FICHA_COMPENSACAO>
                  <RECIBO_PAGADOR>
                     <MENSAGENS>
                        <MENSAGEM>{{clearString (truncate .Title.Instructions 40)}}</MENSAGEM>
                     </MENSAGENS>
                  </RECIBO_PAGADOR>
               </TITULO>
            </INCLUI_BOLETO>
           </DADOS>
      </ext:SERVICO_ENTRADA>
</soapenv:Body>
</soapenv:Envelope>
`

func getRequestCaixa() string {
	return incluiBoleto
}

func getResponseCaixa() string {
	return responseCaixa
}<commit_msg>clearString request caixa<commit_after>package caixa

const responseCaixa = `
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <manutencaocobrancabancaria:SERVICO_SAIDA xmlns:manutencaocobrancabancaria="http://caixa.gov.br/sibar/manutencao_cobranca_bancaria/boleto/externo" xmlns:sibar_base="http://caixa.gov.br/sibar">
            <sibar_base:HEADER>
                <OPERACAO>{{operation}}</OPERACAO>
                <DATA_HORA>{{datetime}}</DATA_HORA>
            </sibar_base:HEADER>
            <DADOS>
                <CONTROLE_NEGOCIAL>
                    <ORIGEM_RETORNO>SIGCB</ORIGEM_RETORNO>
                    <COD_RETORNO>{{returnCode}}</COD_RETORNO>
                    <MENSAGENS>
                        <RETORNO>{{returnMessage}}</RETORNO>
                    </MENSAGENS>
                </CONTROLE_NEGOCIAL>
                <INCLUI_BOLETO>
                    <EXCECAO>{{exception}}</EXCECAO>
                    <CODIGO_BARRAS>{{barcodeNumber}}</CODIGO_BARRAS>
                    <LINHA_DIGITAVEL>{{digitableLine}}</LINHA_DIGITAVEL>
                    <NOSSO_NUMERO>{{ourNumber}}</NOSSO_NUMERO>
                    <URL>{{url}}</URL>
                </INCLUI_BOLETO>
            </DADOS>
        </manutencaocobrancabancaria:SERVICO_SAIDA>
    </soapenv:Body>
</soapenv:Envelope>
`

const incluiBoleto = `

## SOAPAction:IncluiBoleto
## Content-Type:text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ext="http://caixa.gov.br/sibar/manutencao_cobranca_bancaria/boleto/externo" xmlns:sib="http://caixa.gov.br/sibar">
<soapenv:Body>
<ext:SERVICO_ENTRADA >
         <sib:HEADER>
            <VERSAO>1.0</VERSAO>
            <AUTENTICACAO>{{unscape .Authentication.AuthorizationToken}}</AUTENTICACAO>
            <USUARIO_SERVICO>{{caixaEnv}}</USUARIO_SERVICO>
            <OPERACAO>INCLUI_BOLETO</OPERACAO>
            <SISTEMA_ORIGEM>SIGCB</SISTEMA_ORIGEM>
            <UNIDADE>{{.Agreement.Agency}}</UNIDADE>
            <DATA_HORA>{{fullDate today}}</DATA_HORA>
            </sib:HEADER>
         <DADOS>
            <INCLUI_BOLETO>
              <CODIGO_BENEFICIARIO>{{padLeft (toString .Agreement.AgreementNumber) "0" 7}}</CODIGO_BENEFICIARIO>
               <TITULO>
                  <NOSSO_NUMERO>{{toString .Title.OurNumber}}</NOSSO_NUMERO>
                  <NUMERO_DOCUMENTO>{{truncate .Title.DocumentNumber 11}}</NUMERO_DOCUMENTO>
                  <DATA_VENCIMENTO>{{enDate .Title.ExpireDateTime "-"}}</DATA_VENCIMENTO>
                  <VALOR>{{toFloatStr .Title.AmountInCents}}</VALOR>
                  <TIPO_ESPECIE>99</TIPO_ESPECIE>
                  <FLAG_ACEITE>S</FLAG_ACEITE>
                  <DATA_EMISSAO>{{enDate today "-"}}</DATA_EMISSAO>
                  <JUROS_MORA>
                     <TIPO>ISENTO</TIPO>
                     <VALOR>0</VALOR>
                  </JUROS_MORA>
                  <VALOR_ABATIMENTO>0</VALOR_ABATIMENTO>
                  <POS_VENCIMENTO>
                     <ACAO>DEVOLVER</ACAO>
                    <NUMERO_DIAS>0</NUMERO_DIAS>
                  </POS_VENCIMENTO>
                  <CODIGO_MOEDA>9</CODIGO_MOEDA>
                  <PAGADOR>
                     {{if eq .Buyer.Document.Type "CPF"}}
					 	<CPF>{{.Buyer.Document.Number}}</CPF>
                     	<NOME>{{truncate .Buyer.Name 40}}</NOME>
                     {{else}}
					 	<CNPJ>{{.Buyer.Document.Number}}</CNPJ>
                     	<RAZAO_SOCIAL>{{truncate .Buyer.Name 40}}</RAZAO_SOCIAL>
					 {{end}}
                     <ENDERECO>
                     <LOGRADOURO>{{clearString (truncateManyFields 40 .Buyer.Address.Street .Buyer.Address.Number .Buyer.Address.Complement)}}</LOGRADOURO>
                        <BAIRRO>{{clearString (truncate .Buyer.Address.District 15)}}</BAIRRO>
                        <CIDADE>{{clearString (truncate .Buyer.Address.City 15)}}</CIDADE>
                        <UF>{{clearString (truncate .Buyer.Address.StateCode 2)}}</UF>
                        <CEP>{{clearString (truncate .Buyer.Address.ZipCode 8)}}</CEP>
                     </ENDERECO>
                  </PAGADOR>
                  <FICHA_COMPENSACAO>
                     <MENSAGENS>
                        <MENSAGEM>{{clearString (truncate .Title.Instructions 40)}}</MENSAGEM>
                        </MENSAGENS>
                  </FICHA_COMPENSACAO>
                  <RECIBO_PAGADOR>
                     <MENSAGENS>
                        <MENSAGEM>{{clearString (truncate .Title.Instructions 40)}}</MENSAGEM>
                     </MENSAGENS>
                  </RECIBO_PAGADOR>
               </TITULO>
            </INCLUI_BOLETO>
           </DADOS>
      </ext:SERVICO_ENTRADA>
</soapenv:Body>
</soapenv:Envelope>
`

func getRequestCaixa() string {
	return incluiBoleto
}

func getResponseCaixa() string {
	return responseCaixa
}<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors All rights reserved.

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 systemstatsmonitor

import (
	"context"
	"os/exec"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/shirou/gopsutil/disk"

	ssmtypes "k8s.io/node-problem-detector/pkg/systemstatsmonitor/types"
	"k8s.io/node-problem-detector/pkg/util/metrics"
)

type diskCollector struct {
	mIOTime         *metrics.Int64Metric
	mWeightedIO     *metrics.Int64Metric
	mAvgQueueLen    *metrics.Float64Metric
	mOpsCount       *metrics.Int64Metric
	mMergedOpsCount *metrics.Int64Metric
	mOpsBytes       *metrics.Int64Metric
	mOpsTime        *metrics.Int64Metric
	mBytesUsed      *metrics.Int64Metric

	config *ssmtypes.DiskStatsConfig

	lastIOTime           map[string]uint64
	lastWeightedIO       map[string]uint64
	lastReadCount        map[string]uint64
	lastWriteCount       map[string]uint64
	lastMergedReadCount  map[string]uint64
	lastMergedWriteCount map[string]uint64
	lastReadBytes        map[string]uint64
	lastWriteBytes       map[string]uint64
	lastReadTime         map[string]uint64
	lastWriteTime        map[string]uint64

	lastSampleTime time.Time
}

func NewDiskCollectorOrDie(diskConfig *ssmtypes.DiskStatsConfig) *diskCollector {
	dc := diskCollector{config: diskConfig}

	var err error

	// Use metrics.Sum aggregation method to ensure the metric is a counter/cumulative metric.
	dc.mIOTime, err = metrics.NewInt64Metric(
		metrics.DiskIOTimeID,
		diskConfig.MetricsConfigs[string(metrics.DiskIOTimeID)].DisplayName,
		"The IO time spent on the disk, in ms",
		"ms",
		metrics.Sum,
		[]string{deviceNameLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for disk/io_time: %v", err)
	}

	// Use metrics.Sum aggregation method to ensure the metric is a counter/cumulative metric.
	dc.mWeightedIO, err = metrics.NewInt64Metric(
		metrics.DiskWeightedIOID,
		diskConfig.MetricsConfigs[string(metrics.DiskWeightedIOID)].DisplayName,
		"The weighted IO on the disk, in ms",
		"ms",
		metrics.Sum,
		[]string{deviceNameLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for disk/weighted_io: %v", err)
	}

	dc.mAvgQueueLen, err = metrics.NewFloat64Metric(
		metrics.DiskAvgQueueLenID,
		diskConfig.MetricsConfigs[string(metrics.DiskAvgQueueLenID)].DisplayName,
		"The average queue length on the disk",
		"1",
		metrics.LastValue,
		[]string{deviceNameLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for disk/avg_queue_len: %v", err)
	}

	dc.mOpsCount, err = metrics.NewInt64Metric(
		metrics.DiskOpsCountID,
		diskConfig.MetricsConfigs[string(metrics.DiskOpsCountID)].DisplayName,
		"Disk operations count",
		"1",
		metrics.Sum,
		[]string{deviceNameLabel, directionLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskOpsCountID, err)
	}

	dc.mMergedOpsCount, err = metrics.NewInt64Metric(
		metrics.DiskMergedOpsCountID,
		diskConfig.MetricsConfigs[string(metrics.DiskMergedOpsCountID)].DisplayName,
		"Disk merged operations count",
		"1",
		metrics.Sum,
		[]string{deviceNameLabel, directionLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskMergedOpsCountID, err)
	}

	dc.mOpsBytes, err = metrics.NewInt64Metric(
		metrics.DiskOpsBytesID,
		diskConfig.MetricsConfigs[string(metrics.DiskOpsBytesID)].DisplayName,
		"Bytes transferred in disk operations",
		"1",
		metrics.Sum,
		[]string{deviceNameLabel, directionLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskOpsBytesID, err)
	}

	dc.mOpsTime, err = metrics.NewInt64Metric(
		metrics.DiskOpsTimeID,
		diskConfig.MetricsConfigs[string(metrics.DiskOpsTimeID)].DisplayName,
		"Time spent in disk operations, in ms",
		"ms",
		metrics.Sum,
		[]string{deviceNameLabel, directionLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskOpsTimeID, err)
	}

	dc.mBytesUsed, err = metrics.NewInt64Metric(
		metrics.DiskBytesUsedID,
		diskConfig.MetricsConfigs[string(metrics.DiskBytesUsedID)].DisplayName,
		"Disk bytes used, in Bytes",
		"Byte",
		metrics.LastValue,
		[]string{deviceNameLabel, fsTypeLabel, mountOptionLabel, stateLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskBytesUsedID, err)
	}

	dc.lastIOTime = make(map[string]uint64)
	dc.lastWeightedIO = make(map[string]uint64)
	dc.lastReadCount = make(map[string]uint64)
	dc.lastWriteCount = make(map[string]uint64)
	dc.lastMergedReadCount = make(map[string]uint64)
	dc.lastMergedWriteCount = make(map[string]uint64)
	dc.lastReadBytes = make(map[string]uint64)
	dc.lastWriteBytes = make(map[string]uint64)
	dc.lastReadTime = make(map[string]uint64)
	dc.lastWriteTime = make(map[string]uint64)

	return &dc
}

func (dc *diskCollector) recordIOCounters(ioCountersStats map[string]disk.IOCountersStat, sampleTime time.Time) {
	for deviceName, ioCountersStat := range ioCountersStats {
		// Attach label {"device_name": deviceName} to the following metrics.
		tags := map[string]string{deviceNameLabel: deviceName}

		// Calculate average IO queue length since last measurement.
		lastIOTime, historyExist := dc.lastIOTime[deviceName]
		lastWeightedIO := dc.lastWeightedIO[deviceName]
		dc.lastIOTime[deviceName] = ioCountersStat.IoTime
		dc.lastWeightedIO[deviceName] = ioCountersStat.WeightedIO

		if dc.mIOTime != nil {
			dc.mIOTime.Record(tags, int64(ioCountersStat.IoTime-lastIOTime))
		}
		if dc.mWeightedIO != nil {
			dc.mWeightedIO.Record(tags, int64(ioCountersStat.WeightedIO-lastWeightedIO))
		}
		if historyExist {
			avgQueueLen := float64(0.0)
			if lastWeightedIO != ioCountersStat.WeightedIO {
				diffSampleTimeMs := sampleTime.Sub(dc.lastSampleTime).Seconds() * 1000
				avgQueueLen = float64(ioCountersStat.WeightedIO-lastWeightedIO) / diffSampleTimeMs
			}
			if dc.mAvgQueueLen != nil {
				dc.mAvgQueueLen.Record(tags, avgQueueLen)
			}
		}

		// Attach label {"device_name": deviceName, "direction": "read"} to the following metrics.
		tags = map[string]string{deviceNameLabel: deviceName, directionLabel: "read"}

		if dc.mOpsCount != nil {
			dc.mOpsCount.Record(tags, int64(ioCountersStat.ReadCount-dc.lastReadCount[deviceName]))
			dc.lastReadCount[deviceName] = ioCountersStat.ReadCount
		}
		if dc.mMergedOpsCount != nil {
			dc.mMergedOpsCount.Record(tags, int64(ioCountersStat.MergedReadCount-dc.lastMergedReadCount[deviceName]))
			dc.lastMergedReadCount[deviceName] = ioCountersStat.MergedReadCount
		}
		if dc.mOpsBytes != nil {
			dc.mOpsBytes.Record(tags, int64(ioCountersStat.ReadBytes-dc.lastReadBytes[deviceName]))
			dc.lastReadBytes[deviceName] = ioCountersStat.ReadBytes
		}
		if dc.mOpsTime != nil {
			dc.mOpsTime.Record(tags, int64(ioCountersStat.ReadTime-dc.lastReadTime[deviceName]))
			dc.lastReadTime[deviceName] = ioCountersStat.ReadTime
		}

		// Attach label {"device_name": deviceName, "direction": "write"} to the following metrics.
		tags = map[string]string{deviceNameLabel: deviceName, directionLabel: "write"}

		if dc.mOpsCount != nil {
			dc.mOpsCount.Record(tags, int64(ioCountersStat.WriteCount-dc.lastWriteCount[deviceName]))
			dc.lastWriteCount[deviceName] = ioCountersStat.WriteCount
		}
		if dc.mMergedOpsCount != nil {
			dc.mMergedOpsCount.Record(tags, int64(ioCountersStat.MergedWriteCount-dc.lastMergedWriteCount[deviceName]))
			dc.lastMergedWriteCount[deviceName] = ioCountersStat.MergedWriteCount
		}
		if dc.mOpsBytes != nil {
			dc.mOpsBytes.Record(tags, int64(ioCountersStat.WriteBytes-dc.lastWriteBytes[deviceName]))
			dc.lastWriteBytes[deviceName] = ioCountersStat.WriteBytes
		}
		if dc.mOpsTime != nil {
			dc.mOpsTime.Record(tags, int64(ioCountersStat.WriteTime-dc.lastWriteTime[deviceName]))
			dc.lastWriteTime[deviceName] = ioCountersStat.WriteTime
		}
	}
}

func (dc *diskCollector) collect() {
	if dc == nil {
		return
	}

	// List available devices.
	devices := []string{}
	if dc.config.IncludeRootBlk {
		devices = append(devices, listRootBlockDevices(dc.config.LsblkTimeout)...)
	}
	if dc.config.IncludeAllAttachedBlk {
		devices = append(devices, listAttachedBlockDevices()...)
	}

	// Fetch metrics from /proc, /sys.
	ioCountersStats, err := disk.IOCounters(devices...)
	if err != nil {
		glog.Errorf("Failed to retrieve disk IO counters: %v", err)
		return
	}
	partitions, err := disk.Partitions(false)
	if err != nil {
		glog.Errorf("Failed to list disk partitions: %v", err)
		return
	}
	sampleTime := time.Now()
	defer func() { dc.lastSampleTime = sampleTime }()

	// Record metrics regarding disk IO.
	dc.recordIOCounters(ioCountersStats, sampleTime)

	// Record metrics regarding disk space usage.
	if dc.mBytesUsed == nil {
		return
	}
	for _, partition := range partitions {
		usageStat, err := disk.Usage(partition.Mountpoint)
		if err != nil {
			glog.Errorf("Failed to retrieve disk usage for %q: %v", partition.Mountpoint, err)
			continue
		}
		deviceName := strings.TrimPrefix(partition.Device, "/dev/")
		fstype := partition.Fstype
		opttypes := partition.Opts
		dc.mBytesUsed.Record(map[string]string{deviceNameLabel: deviceName, fsTypeLabel: fstype, mountOptionLabel: opttypes, stateLabel: "free"}, int64(usageStat.Free))
		dc.mBytesUsed.Record(map[string]string{deviceNameLabel: deviceName, fsTypeLabel: fstype, mountOptionLabel: opttypes, stateLabel: "used"}, int64(usageStat.Used))
	}

}

// listRootBlockDevices lists all block devices that's not a slave or holder.
func listRootBlockDevices(timeout time.Duration) []string {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	// "-d" prevents printing slave or holder devices. i.e. /dev/sda1, /dev/sda2...
	// "-n" prevents printing the headings.
	// "-p NAME" specifies to only print the device name.
	cmd := exec.CommandContext(ctx, "lsblk", "-d", "-n", "-o", "NAME")
	stdout, err := cmd.Output()
	if err != nil {
		glog.Errorf("Error calling lsblk")
	}
	return strings.Split(strings.TrimSpace(string(stdout)), "\n")
}

// listAttachedBlockDevices lists all currently attached block devices.
func listAttachedBlockDevices() []string {
	blks := []string{}

	partitions, err := disk.Partitions(false)
	if err != nil {
		glog.Errorf("Failed to retrieve the list of disk partitions: %v", err)
		return blks
	}

	for _, partition := range partitions {
		blks = append(blks, partition.Device)
	}
	return blks
}
<commit_msg>avoid duplicating the disk bytes used metrics based on fstype and mountopts<commit_after>/*
Copyright 2019 The Kubernetes Authors All rights reserved.

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 systemstatsmonitor

import (
	"context"
	"os/exec"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/shirou/gopsutil/disk"

	ssmtypes "k8s.io/node-problem-detector/pkg/systemstatsmonitor/types"
	"k8s.io/node-problem-detector/pkg/util/metrics"
)

type diskCollector struct {
	mIOTime         *metrics.Int64Metric
	mWeightedIO     *metrics.Int64Metric
	mAvgQueueLen    *metrics.Float64Metric
	mOpsCount       *metrics.Int64Metric
	mMergedOpsCount *metrics.Int64Metric
	mOpsBytes       *metrics.Int64Metric
	mOpsTime        *metrics.Int64Metric
	mBytesUsed      *metrics.Int64Metric

	config *ssmtypes.DiskStatsConfig

	lastIOTime           map[string]uint64
	lastWeightedIO       map[string]uint64
	lastReadCount        map[string]uint64
	lastWriteCount       map[string]uint64
	lastMergedReadCount  map[string]uint64
	lastMergedWriteCount map[string]uint64
	lastReadBytes        map[string]uint64
	lastWriteBytes       map[string]uint64
	lastReadTime         map[string]uint64
	lastWriteTime        map[string]uint64

	lastSampleTime time.Time
}

func NewDiskCollectorOrDie(diskConfig *ssmtypes.DiskStatsConfig) *diskCollector {
	dc := diskCollector{config: diskConfig}

	var err error

	// Use metrics.Sum aggregation method to ensure the metric is a counter/cumulative metric.
	dc.mIOTime, err = metrics.NewInt64Metric(
		metrics.DiskIOTimeID,
		diskConfig.MetricsConfigs[string(metrics.DiskIOTimeID)].DisplayName,
		"The IO time spent on the disk, in ms",
		"ms",
		metrics.Sum,
		[]string{deviceNameLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for disk/io_time: %v", err)
	}

	// Use metrics.Sum aggregation method to ensure the metric is a counter/cumulative metric.
	dc.mWeightedIO, err = metrics.NewInt64Metric(
		metrics.DiskWeightedIOID,
		diskConfig.MetricsConfigs[string(metrics.DiskWeightedIOID)].DisplayName,
		"The weighted IO on the disk, in ms",
		"ms",
		metrics.Sum,
		[]string{deviceNameLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for disk/weighted_io: %v", err)
	}

	dc.mAvgQueueLen, err = metrics.NewFloat64Metric(
		metrics.DiskAvgQueueLenID,
		diskConfig.MetricsConfigs[string(metrics.DiskAvgQueueLenID)].DisplayName,
		"The average queue length on the disk",
		"1",
		metrics.LastValue,
		[]string{deviceNameLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for disk/avg_queue_len: %v", err)
	}

	dc.mOpsCount, err = metrics.NewInt64Metric(
		metrics.DiskOpsCountID,
		diskConfig.MetricsConfigs[string(metrics.DiskOpsCountID)].DisplayName,
		"Disk operations count",
		"1",
		metrics.Sum,
		[]string{deviceNameLabel, directionLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskOpsCountID, err)
	}

	dc.mMergedOpsCount, err = metrics.NewInt64Metric(
		metrics.DiskMergedOpsCountID,
		diskConfig.MetricsConfigs[string(metrics.DiskMergedOpsCountID)].DisplayName,
		"Disk merged operations count",
		"1",
		metrics.Sum,
		[]string{deviceNameLabel, directionLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskMergedOpsCountID, err)
	}

	dc.mOpsBytes, err = metrics.NewInt64Metric(
		metrics.DiskOpsBytesID,
		diskConfig.MetricsConfigs[string(metrics.DiskOpsBytesID)].DisplayName,
		"Bytes transferred in disk operations",
		"1",
		metrics.Sum,
		[]string{deviceNameLabel, directionLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskOpsBytesID, err)
	}

	dc.mOpsTime, err = metrics.NewInt64Metric(
		metrics.DiskOpsTimeID,
		diskConfig.MetricsConfigs[string(metrics.DiskOpsTimeID)].DisplayName,
		"Time spent in disk operations, in ms",
		"ms",
		metrics.Sum,
		[]string{deviceNameLabel, directionLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskOpsTimeID, err)
	}

	dc.mBytesUsed, err = metrics.NewInt64Metric(
		metrics.DiskBytesUsedID,
		diskConfig.MetricsConfigs[string(metrics.DiskBytesUsedID)].DisplayName,
		"Disk bytes used, in Bytes",
		"Byte",
		metrics.LastValue,
		[]string{deviceNameLabel, fsTypeLabel, mountOptionLabel, stateLabel})
	if err != nil {
		glog.Fatalf("Error initializing metric for %q: %v", metrics.DiskBytesUsedID, err)
	}

	dc.lastIOTime = make(map[string]uint64)
	dc.lastWeightedIO = make(map[string]uint64)
	dc.lastReadCount = make(map[string]uint64)
	dc.lastWriteCount = make(map[string]uint64)
	dc.lastMergedReadCount = make(map[string]uint64)
	dc.lastMergedWriteCount = make(map[string]uint64)
	dc.lastReadBytes = make(map[string]uint64)
	dc.lastWriteBytes = make(map[string]uint64)
	dc.lastReadTime = make(map[string]uint64)
	dc.lastWriteTime = make(map[string]uint64)

	return &dc
}

func (dc *diskCollector) recordIOCounters(ioCountersStats map[string]disk.IOCountersStat, sampleTime time.Time) {
	for deviceName, ioCountersStat := range ioCountersStats {
		// Attach label {"device_name": deviceName} to the following metrics.
		tags := map[string]string{deviceNameLabel: deviceName}

		// Calculate average IO queue length since last measurement.
		lastIOTime, historyExist := dc.lastIOTime[deviceName]
		lastWeightedIO := dc.lastWeightedIO[deviceName]
		dc.lastIOTime[deviceName] = ioCountersStat.IoTime
		dc.lastWeightedIO[deviceName] = ioCountersStat.WeightedIO

		if dc.mIOTime != nil {
			dc.mIOTime.Record(tags, int64(ioCountersStat.IoTime-lastIOTime))
		}
		if dc.mWeightedIO != nil {
			dc.mWeightedIO.Record(tags, int64(ioCountersStat.WeightedIO-lastWeightedIO))
		}
		if historyExist {
			avgQueueLen := float64(0.0)
			if lastWeightedIO != ioCountersStat.WeightedIO {
				diffSampleTimeMs := sampleTime.Sub(dc.lastSampleTime).Seconds() * 1000
				avgQueueLen = float64(ioCountersStat.WeightedIO-lastWeightedIO) / diffSampleTimeMs
			}
			if dc.mAvgQueueLen != nil {
				dc.mAvgQueueLen.Record(tags, avgQueueLen)
			}
		}

		// Attach label {"device_name": deviceName, "direction": "read"} to the following metrics.
		tags = map[string]string{deviceNameLabel: deviceName, directionLabel: "read"}

		if dc.mOpsCount != nil {
			dc.mOpsCount.Record(tags, int64(ioCountersStat.ReadCount-dc.lastReadCount[deviceName]))
			dc.lastReadCount[deviceName] = ioCountersStat.ReadCount
		}
		if dc.mMergedOpsCount != nil {
			dc.mMergedOpsCount.Record(tags, int64(ioCountersStat.MergedReadCount-dc.lastMergedReadCount[deviceName]))
			dc.lastMergedReadCount[deviceName] = ioCountersStat.MergedReadCount
		}
		if dc.mOpsBytes != nil {
			dc.mOpsBytes.Record(tags, int64(ioCountersStat.ReadBytes-dc.lastReadBytes[deviceName]))
			dc.lastReadBytes[deviceName] = ioCountersStat.ReadBytes
		}
		if dc.mOpsTime != nil {
			dc.mOpsTime.Record(tags, int64(ioCountersStat.ReadTime-dc.lastReadTime[deviceName]))
			dc.lastReadTime[deviceName] = ioCountersStat.ReadTime
		}

		// Attach label {"device_name": deviceName, "direction": "write"} to the following metrics.
		tags = map[string]string{deviceNameLabel: deviceName, directionLabel: "write"}

		if dc.mOpsCount != nil {
			dc.mOpsCount.Record(tags, int64(ioCountersStat.WriteCount-dc.lastWriteCount[deviceName]))
			dc.lastWriteCount[deviceName] = ioCountersStat.WriteCount
		}
		if dc.mMergedOpsCount != nil {
			dc.mMergedOpsCount.Record(tags, int64(ioCountersStat.MergedWriteCount-dc.lastMergedWriteCount[deviceName]))
			dc.lastMergedWriteCount[deviceName] = ioCountersStat.MergedWriteCount
		}
		if dc.mOpsBytes != nil {
			dc.mOpsBytes.Record(tags, int64(ioCountersStat.WriteBytes-dc.lastWriteBytes[deviceName]))
			dc.lastWriteBytes[deviceName] = ioCountersStat.WriteBytes
		}
		if dc.mOpsTime != nil {
			dc.mOpsTime.Record(tags, int64(ioCountersStat.WriteTime-dc.lastWriteTime[deviceName]))
			dc.lastWriteTime[deviceName] = ioCountersStat.WriteTime
		}
	}
}

func (dc *diskCollector) collect() {
	if dc == nil {
		return
	}

	// List available devices.
	devices := []string{}
	if dc.config.IncludeRootBlk {
		devices = append(devices, listRootBlockDevices(dc.config.LsblkTimeout)...)
	}
	if dc.config.IncludeAllAttachedBlk {
		devices = append(devices, listAttachedBlockDevices()...)
	}

	// Fetch metrics from /proc, /sys.
	ioCountersStats, err := disk.IOCounters(devices...)
	if err != nil {
		glog.Errorf("Failed to retrieve disk IO counters: %v", err)
		return
	}
	partitions, err := disk.Partitions(false)
	if err != nil {
		glog.Errorf("Failed to list disk partitions: %v", err)
		return
	}
	sampleTime := time.Now()
	defer func() { dc.lastSampleTime = sampleTime }()

	// Record metrics regarding disk IO.
	dc.recordIOCounters(ioCountersStats, sampleTime)

	// Record metrics regarding disk space usage.
	if dc.mBytesUsed == nil {
		return
	}

	// to make sure that the rows are not duplicated
	// we display only the only one row even if there are
	// mutiple rows for the same disk.
	seen := make(map[string]bool)
	for _, partition := range partitions {
		if seen[partition.Device] {
			continue
		}
		seen[partition.Device] = true
		usageStat, err := disk.Usage(partition.Mountpoint)
		if err != nil {
			glog.Errorf("Failed to retrieve disk usage for %q: %v", partition.Mountpoint, err)
			continue
		}
		deviceName := strings.TrimPrefix(partition.Device, "/dev/")
		fstype := partition.Fstype
		opttypes := partition.Opts
		dc.mBytesUsed.Record(map[string]string{deviceNameLabel: deviceName, fsTypeLabel: fstype, mountOptionLabel: opttypes, stateLabel: "free"}, int64(usageStat.Free))
		dc.mBytesUsed.Record(map[string]string{deviceNameLabel: deviceName, fsTypeLabel: fstype, mountOptionLabel: opttypes, stateLabel: "used"}, int64(usageStat.Used))
	}

}

// listRootBlockDevices lists all block devices that's not a slave or holder.
func listRootBlockDevices(timeout time.Duration) []string {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	// "-d" prevents printing slave or holder devices. i.e. /dev/sda1, /dev/sda2...
	// "-n" prevents printing the headings.
	// "-p NAME" specifies to only print the device name.
	cmd := exec.CommandContext(ctx, "lsblk", "-d", "-n", "-o", "NAME")
	stdout, err := cmd.Output()
	if err != nil {
		glog.Errorf("Error calling lsblk")
	}
	return strings.Split(strings.TrimSpace(string(stdout)), "\n")
}

// listAttachedBlockDevices lists all currently attached block devices.
func listAttachedBlockDevices() []string {
	blks := []string{}

	partitions, err := disk.Partitions(false)
	if err != nil {
		glog.Errorf("Failed to retrieve the list of disk partitions: %v", err)
		return blks
	}

	for _, partition := range partitions {
		blks = append(blks, partition.Device)
	}
	return blks
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"

	"github.com/antonve/logger-api/models/enums"
	"github.com/jmoiron/sqlx/types"
)

// LogCollection array of logs
type LogCollection struct {
	Logs []Log `json:"logs"`
}

// Log model
type Log struct {
	ID       uint64         `json:"id" db:"id"`
	UserID   uint64         `json:"user_id" db:"user_id"`
	Language enums.Language `json:"language" db:"language"`
	Date     string         `json:"date" db:"date"`
	Duration uint64         `json:"duration" db:"duration"`
	Activity enums.Activity `json:"activity" db:"activity"`
	Notes    types.JSONText `json:"notes" db:"notes"`
}

// Length returns the amount of logs in the collection
func (logCollection *LogCollection) Length() int {
	return len(logCollection.Logs)
}

// Validate the Log model
func (log *Log) Validate() error {
	if log.UserID == 0 {
		return errors.New("invalid `UserID` supplied")
	}
	if log.Date == "" {
		return errors.New("invalid `Date` supplied")
	}
	if log.Duration == 0 {
		return errors.New("invalid `Duration` supplied")
	}
	if len(log.Activity) == 0 || !log.Activity.IsValid() {
		return errors.New("invalid `Activity` supplied")
	}

	return nil
}

// GetAll returns all logs
func (logCollection *LogCollection) GetAll() error {
	db := GetDatabase()
	defer db.Close()

	err := db.Select(&logCollection.Logs, `
		SELECT
			id,
			user_id,
			language,
			to_char(date, 'YYYY-MM-DD') AS date,
			duration,
			activity,
			notes
		FROM logs
		WHERE deleted = FALSE
	`)

	return err
}

// GetAllFromUser returns all logs from a certain user
func (logCollection *LogCollection) GetAllFromUser(userID uint64) error {
	db := GetDatabase()
	defer db.Close()

	err := db.Select(&logCollection.Logs, `
		SELECT
			id,
			user_id,
			language,
			to_char(date, 'YYYY-MM-DD') AS date,
			duration,
			activity,
			notes
		FROM logs
		WHERE
			user_id = $1 AND
		  deleted = FALSE
	`, userID)

	return err
}

// Get a log by id
func (logCollection *LogCollection) Get(id uint64) (*Log, error) {
	db := GetDatabase()
	defer db.Close()

	// Init log
	log := Log{}

	// Get log
	stmt, err := db.Preparex(`
		SELECT
			id,
			user_id,
			language,
			to_char(date, 'YYYY-MM-DD') AS date,
			duration,
			activity,
			notes
		FROM logs
		WHERE
			id = $1 AND
			deleted = FALSE
	`)
	if err != nil {
		return nil, err
	}

	stmt.Get(&log, id)
	if log.ID == 0 {
		return nil, fmt.Errorf("no log found with id %v", id)
	}

	return &log, nil
}

// Add a log to the database
func (logCollection *LogCollection) Add(log *Log) (uint64, error) {
	db := GetDatabase()
	defer db.Close()

	query := `
		INSERT INTO logs (user_id, language, date, duration, activity, notes)
		VALUES (:user_id, :language, :date, :duration, :activity, :notes)
		RETURNING id
	`
	rows, err := db.NamedQuery(query, log)

	if err != nil {
		return 0, err
	}

	var id uint64
	if rows.Next() {
		rows.Scan(&id)
	}

	return id, nil
}

// Update a log
func (logCollection *LogCollection) Update(log *Log) error {
	db := GetDatabase()
	defer db.Close()

	query := `
		UPDATE logs
		SET
			language = :language,
			date = :date,
			duration = :duration,
			activity = :activity,
			notes = :notes
		WHERE
			id = :id AND
			user_id = :user_id AND
			deleted = FALSE
	`
	result, err := db.NamedExec(query, log)
	if err != nil {
		return err
	}

	rows, err := result.RowsAffected()
	if rows == 0 {
		err = fmt.Errorf("no log found with id %d for user %d", log.ID, log.UserID)
	}

	return err
}

// Delete a log
func (logCollection *LogCollection) Delete(log *Log) error {
	db := GetDatabase()
	defer db.Close()

	query := `
		UPDATE logs
		SET deleted = TRUE
		WHERE
			id = :id AND
			deleted = FALSE
	`
	result, err := db.NamedExec(query, log)
	if err != nil {
		return err
	}

	rows, err := result.RowsAffected()
	if rows == 0 {
		err = fmt.Errorf("no log found with id %v or it has already been deleted", log.ID)
	}

	return err
}

// IsOwner checks the owner
func (log *Log) IsOwner(userID uint64) bool {
	if log.UserID == userID {
		return true
	}

	return false
}
<commit_msg>Add query to filter by dates<commit_after>package models

import (
	"errors"
	"fmt"

	"github.com/antonve/logger-api/models/enums"
	"github.com/jmoiron/sqlx/types"
)

// LogCollection array of logs
type LogCollection struct {
	Logs []Log `json:"logs"`
}

// Log model
type Log struct {
	ID       uint64         `json:"id" db:"id"`
	UserID   uint64         `json:"user_id" db:"user_id"`
	Language enums.Language `json:"language" db:"language"`
	Date     string         `json:"date" db:"date"`
	Duration uint64         `json:"duration" db:"duration"`
	Activity enums.Activity `json:"activity" db:"activity"`
	Notes    types.JSONText `json:"notes" db:"notes"`
}

// Length returns the amount of logs in the collection
func (logCollection *LogCollection) Length() int {
	return len(logCollection.Logs)
}

// Validate the Log model
func (log *Log) Validate() error {
	if log.UserID == 0 {
		return errors.New("invalid `UserID` supplied")
	}
	if log.Date == "" {
		return errors.New("invalid `Date` supplied")
	}
	if log.Duration == 0 {
		return errors.New("invalid `Duration` supplied")
	}
	if len(log.Activity) == 0 || !log.Activity.IsValid() {
		return errors.New("invalid `Activity` supplied")
	}

	return nil
}

// GetAll returns all logs
func (logCollection *LogCollection) GetAll() error {
	db := GetDatabase()
	defer db.Close()

	err := db.Select(&logCollection.Logs, `
		SELECT
			id,
			user_id,
			language,
			to_char(date, 'YYYY-MM-DD') AS date,
			duration,
			activity,
			notes
		FROM logs
		WHERE deleted = FALSE
	`)

	return err
}

// GetAllFromUser returns all logs from a certain user
func (logCollection *LogCollection) GetAllFromUser(userID uint64) error {
	db := GetDatabase()
	defer db.Close()

	err := db.Select(&logCollection.Logs, `
		SELECT
			id,
			user_id,
			language,
			to_char(date, 'YYYY-MM-DD') AS date,
			duration,
			activity,
			notes
		FROM logs
		WHERE
			user_id = $1 AND
		  deleted = FALSE
	`, userID)

	return err
}

// GetAllFromUserByDate returns all logs from a certain user on a certain date
func (logCollection *LogCollection) GetAllFromUserByDate(userID uint64, date string) error {
	return logCollection.GetAllFromUserByDateRange(userID, date, date)
}

// GetAllFromUserByDateRange returns all logs from a certain user in a certain date range
func (logCollection *LogCollection) GetAllFromUserByDateRange(userID uint64, dateStart string, dateEnd string) error {
	db := GetDatabase()
	defer db.Close()

	err := db.Select(&logCollection.Logs, `
		SELECT
			id,
			user_id,
			language,
			to_char(date, 'YYYY-MM-DD') AS date,
			duration,
			activity,
			notes
		FROM logs
		WHERE
			user_id = $1 AND
			date >= dateStart AND
			date <= dateEnd AND
		  deleted = FALSE
	`, userID, dateStart, dateEnd)

	return err
}

// Get a log by id
func (logCollection *LogCollection) Get(id uint64) (*Log, error) {
	db := GetDatabase()
	defer db.Close()

	// Init log
	log := Log{}

	// Get log
	stmt, err := db.Preparex(`
		SELECT
			id,
			user_id,
			language,
			to_char(date, 'YYYY-MM-DD') AS date,
			duration,
			activity,
			notes
		FROM logs
		WHERE
			id = $1 AND
			deleted = FALSE
	`)
	if err != nil {
		return nil, err
	}

	stmt.Get(&log, id)
	if log.ID == 0 {
		return nil, fmt.Errorf("no log found with id %v", id)
	}

	return &log, nil
}

// Add a log to the database
func (logCollection *LogCollection) Add(log *Log) (uint64, error) {
	db := GetDatabase()
	defer db.Close()

	query := `
		INSERT INTO logs (user_id, language, date, duration, activity, notes)
		VALUES (:user_id, :language, :date, :duration, :activity, :notes)
		RETURNING id
	`
	rows, err := db.NamedQuery(query, log)

	if err != nil {
		return 0, err
	}

	var id uint64
	if rows.Next() {
		rows.Scan(&id)
	}

	return id, nil
}

// Update a log
func (logCollection *LogCollection) Update(log *Log) error {
	db := GetDatabase()
	defer db.Close()

	query := `
		UPDATE logs
		SET
			language = :language,
			date = :date,
			duration = :duration,
			activity = :activity,
			notes = :notes
		WHERE
			id = :id AND
			user_id = :user_id AND
			deleted = FALSE
	`
	result, err := db.NamedExec(query, log)
	if err != nil {
		return err
	}

	rows, err := result.RowsAffected()
	if rows == 0 {
		err = fmt.Errorf("no log found with id %d for user %d", log.ID, log.UserID)
	}

	return err
}

// Delete a log
func (logCollection *LogCollection) Delete(log *Log) error {
	db := GetDatabase()
	defer db.Close()

	query := `
		UPDATE logs
		SET deleted = TRUE
		WHERE
			id = :id AND
			deleted = FALSE
	`
	result, err := db.NamedExec(query, log)
	if err != nil {
		return err
	}

	rows, err := result.RowsAffected()
	if rows == 0 {
		err = fmt.Errorf("no log found with id %v or it has already been deleted", log.ID)
	}

	return err
}

// IsOwner checks the owner
func (log *Log) IsOwner(userID uint64) bool {
	if log.UserID == userID {
		return true
	}

	return false
}
<|endoftext|>


<commit_before>// Copyright 2016 Eleme Inc. All rights reserved.

package models

import (
	"github.com/eleme/banshee/util"
)

// Conditions
const (
	WhenAnomalousTrend                  = 0x0
	WhenAnomalousTrendUp                = 0x1
	WhenAnomalousTrendDown              = 0x2
	WhenAnomalousTrendUpTo              = 0x3
	WhenAnomalousTrendDownTo            = 0x4
	WhenAnomalousTrendUpToOrTrendDown   = 0x5
	WhenAnomalousTrendDownToOrTrendUp   = 0x6
	WhenAnomalousTrendUpToOrTrendDownTo = 0x7
	WhenValueGreaterThan                = 0x8
	WhenValueLessThan                   = 0x9
	WhenValueGreaterThanOrLessThan      = 0xa
)

// Rule is a type to describe alerting rule.
type Rule struct {
	// Pattern is a wildcard string
	Pattern string
	// Condition
	When         int
	ThresholdMax float64
	ThresholdMin float64
}

// Test Metric with Rule.
func (rule *Rule) Test(m *Metric) bool {
	if !util.FnMatch(m.Name, rule.Pattern) {
		return false
	}
	switch rule.When {
	case WhenAnomalousTrend:
		return m.IsAnomalous()
	case WhenAnomalousTrendUp:
		return m.IsAnomalousTrendUp()
	case WhenAnomalousTrendDown:
		return m.IsAnomalousTrendDown()
	case WhenAnomalousTrendUpTo:
		return m.IsAnomalousTrendUp() && m.Value >= rule.ThresholdMin
	case WhenAnomalousTrendDownTo:
		return m.IsAnomalousTrendDown() && m.Value <= rule.ThresholdMax
	case WhenAnomalousTrendUpToOrTrendDown:
		return (m.IsAnomalousTrendUp() && m.Value >= rule.ThresholdMin) || m.IsAnomalousTrendDown()
	case WhenAnomalousTrendDownToOrTrendUp:
		return (m.IsAnomalousTrendDown() && m.Value <= rule.ThresholdMax) || m.IsAnomalousTrendUp()
	case WhenAnomalousTrendUpToOrTrendDownTo:
		return (m.IsAnomalousTrendUp() && m.Value >= rule.ThresholdMin) || (m.IsAnomalousTrendDown() && m.Value <= rule.ThresholdMax)
	case WhenValueGreaterThan:
		return m.Value >= rule.ThresholdMin
	case WhenValueLessThan:
		return m.Value <= rule.ThresholdMax
	case WhenValueGreaterThanOrLessThan:
		return m.Value <= rule.ThresholdMax || m.Value >= rule.ThresholdMin
	}
	return false
}

// Validate rule.When
func (rule *Rule) IsValid() bool {
	return rule.When >= 0x0 && rule.When <= 0xa
}
<commit_msg>re-design Rule<commit_after>// Copyright 2016 Eleme Inc. All rights reserved.

package models

import (
	"github.com/eleme/banshee/util"
)

// Conditions 	000001 = 0x1 	000010 = 0x2	000100 = 0x4
//				001000 = 0x8	010000 = 0x10	100000 = 0x20
const (
	WhenAnomalousTrendUp   = 0x1
	WhenAnomalousTrendDown = 0x2
	//When anomalous greater than thresholdMax AND trend up
	WhenAnomalousTrendUpTo = 0x4
	//When anomalous less than thresholdMin AND trend Down
	WhenAnomalousTrendDownTo = 0x8
	WhenValueGreaterThan     = 0x10
	WhenValueLessThan        = 0x20
)

// Rule is a type to describe alerting rule.
type Rule struct {
	// Pattern is a wildcard string
	Pattern string
	// Condition
	//When = sum (conditions const) , for example :
	//0x15 when WhenAnomalousTrendUp OR WhenAnomalousTrendUpTo OR WhenValueGreaterThan
	When         int
	ThresholdMax float64
	ThresholdMin float64
}

// Test Metric with Rule.
func (rule *Rule) Test(m *Metric) bool {
	if !util.FnMatch(m.Name, rule.Pattern) {
		return false
	}
	test := false
	tmp := rule.When
	for k := 0x20; k >= 0x1; k = k / 2 {
		if tmp >= k {
			tmp = tmp - k
			switch k {
			case WhenAnomalousTrendUp:
				test = test || m.IsAnomalousTrendUp()
			case WhenAnomalousTrendDown:
				test = test || m.IsAnomalousTrendDown()
			case WhenValueGreaterThan:
				test = test || m.Value >= rule.ThresholdMax
			case WhenValueLessThan:
				test = test || m.Value <= rule.ThresholdMin
			case WhenAnomalousTrendUpTo:
				test = test || (m.IsAnomalousTrendUp() && m.Value >= rule.ThresholdMax)
			case WhenAnomalousTrendDownTo:
				test = test || (m.IsAnomalousTrendDown() && m.Value <= rule.ThresholdMin)
			}
		}
		if test {
			return test
		}
	}
	return test
}

// Validate rule.When
func (rule *Rule) IsValid() bool {
	return rule.When > 0x1 && rule.When <= 0x3F
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"os/exec"
	"strings"
	"time"

	"gopkg.in/robfig/cron.v2"

	"github.com/jinzhu/gorm"
	"github.com/mrkaspa/go-helpers"
)

//Task executed recurrently
type Task struct {
	ID          string    `sql:"type:varchar(100)" gorm:"primary_key" json:"id"`
	Periodicity string    `json:"periodicity" validate:"required"`
	CronID      int       `json:"-"`
	Command     string    `json:"command" validate:"required"`
	CreatedAt   time.Time `json:"created_at"`
}

//BeforeCreate callback
func (t *Task) BeforeCreate() {
	if t.ID == "" {
		t.ID = helpers.PseudoUUID()
	}
}

//AfterCreate callback
func (t *Task) AfterCreate(txn *gorm.DB) error {
	return t.Start(txn)
}

//AfterUpdate callback
func (t *Task) AfterUpdate(txn *gorm.DB) error {
	if err := t.Stop(txn); err != nil {
		return err
	}
	return t.Start(txn)
}

//BeforeDelete callback
func (t *Task) BeforeDelete(txn *gorm.DB) error {
	return t.Stop(txn)
}

func (t *Task) Start(txn *gorm.DB) error {
	fmt.Println("TASK")
	fmt.Println(t)
	pid, err := MasterCron.AddFunc(t.Periodicity, func() {
		commandArr := strings.Split(t.Command, " ")
		command, args := commandArr[0], commandArr[1:]
		exec.Command(command, args...).Run()
	})
	if err != nil {
		fmt.Println("ERR L56")
		return err
	}
	fmt.Println("Cron started")
	return txn.Model(t).UpdateColumn("cron_id", int(pid)).Error
}

func (t *Task) Stop(txn *gorm.DB) error {
	MasterCron.Remove(cron.EntryID(t.CronID))
	return txn.Model(t).UpdateColumn("cron_id", 0).Error
}
<commit_msg>added some logs<commit_after>package models

import (
	"fmt"
	"os/exec"
	"strings"
	"time"

	"gopkg.in/robfig/cron.v2"

	"github.com/jinzhu/gorm"
	"github.com/mrkaspa/go-helpers"
)

//Task executed recurrently
type Task struct {
	ID          string    `sql:"type:varchar(100)" gorm:"primary_key" json:"id"`
	Periodicity string    `json:"periodicity" validate:"required"`
	CronID      int       `json:"-"`
	Command     string    `json:"command" validate:"required"`
	CreatedAt   time.Time `json:"created_at"`
}

//BeforeCreate callback
func (t *Task) BeforeCreate() {
	if t.ID == "" {
		t.ID = helpers.PseudoUUID()
	}
}

//AfterCreate callback
func (t *Task) AfterCreate(txn *gorm.DB) error {
	return t.Start(txn)
}

//AfterUpdate callback
func (t *Task) AfterUpdate(txn *gorm.DB) error {
	if err := t.Stop(txn); err != nil {
		return err
	}
	return t.Start(txn)
}

//BeforeDelete callback
func (t *Task) BeforeDelete(txn *gorm.DB) error {
	return t.Stop(txn)
}

func (t *Task) Start(txn *gorm.DB) error {
	fmt.Println("TASK")
	fmt.Println(t.Command)
	pid, err := MasterCron.AddFunc(t.Periodicity, func() {
		commandArr := strings.Split(t.Command, " ")
		command, args := commandArr[0], commandArr[1:]
		exec.Command(command, args...).Run()
	})
	if err != nil {
		fmt.Println("ERR L56")
		return err
	}
	fmt.Println("Cron started")
	return txn.Model(t).UpdateColumn("cron_id", int(pid)).Error
}

func (t *Task) Stop(txn *gorm.DB) error {
	MasterCron.Remove(cron.EntryID(t.CronID))
	return txn.Model(t).UpdateColumn("cron_id", 0).Error
}
<|endoftext|>


<commit_before>package models

import (
	"time"
)

type User struct {
	ID           int       `db:"id"`
	Username     string    `db:"username"`
	Password     string    `db:"password"`
	CreationDate time.Time `db:"creation_date"`
}

// Inheriting from Model interface
func (u User) TableName() string {
	return "users"
}
<commit_msg>Change to uint<commit_after>package models

import (
	"time"
)

type User struct {
	ID           uint      `db:"id"`
	Username     string    `db:"username"`
	Password     string    `db:"password"`
	CreationDate time.Time `db:"creation_date"`
}

// Inheriting from Model interface
func (u User) TableName() string {
	return "users"
}
<|endoftext|>


<commit_before>// stardew.rocks web server
//
// It contains a few HTTP handlers:
// - a static handler that serves files from /home/stardew/www.
// - a handler that serves the latest complete file save in XML format.
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/nictuku/stardew-rocks/stardb"
	"github.com/nytimes/gziphandler"
)

func logzip(handler http.Handler) http.Handler {
	return gziphandler.GzipHandler(logHandler(handler))
}

func logHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL)
		handler.ServeHTTP(w, r)
	})
}

func wwwDir() string {
	home := os.Getenv("HOME")
	if home == "" {
		home = string(filepath.Separator)
	}
	return filepath.Clean(filepath.Join(home, "www"))
}

func GetFarms(w http.ResponseWriter, r *http.Request) {
	b, err := stardb.FarmsJSON()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	_, err = w.Write(b)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	return
}

func GetFarm(w http.ResponseWriter, r *http.Request) {
	b, err := stardb.FarmsJSON()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	_, err = w.Write(b)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	return
}

func ServeGFSFile(w http.ResponseWriter, r *http.Request) {
	if !strings.HasPrefix(r.URL.Path, "/screenshot") {
		http.Error(w, "Not found"+r.URL.Path, http.StatusNotFound)
		return
	}
	s := strings.Split(r.URL.Path, "/")
	if len(s) != 4 {
		http.Error(w, fmt.Sprintf("...%v - %d", s, len(s)), http.StatusNotFound)
		return
	}
	if s[1] != "screenshot" && s[1] != "saveGames" {
		http.Error(w, "no.. Not found", http.StatusNotFound)
		return
	}

	f, err := stardb.GFS.Open(r.URL.Path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	_, err = io.Copy(w, f)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	return
}

func RunHTTPServer() {
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Println("Error starting www server:", err)
		// os.IsPermission doesn't match.
		if strings.Contains(err.Error(), "permission denied") {
			// Only for linux and port 80. Keeping here for future convenience.
			log.Println("Try: sudo setcap 'cap_net_bind_service=+ep' www")
		}
	}
}

func main() {
	dir := wwwDir()
	log.Printf("Serving files from %v", dir)
	http.Handle("/", logzip(http.FileServer(http.Dir(dir))))
	http.Handle("/api/farms", logHandler(http.HandlerFunc(GetFarms)))
	http.Handle("/screenshot/", logHandler(http.HandlerFunc(ServeGFSFile)))
	http.Handle("/saveGames/", logHandler(http.HandlerFunc(ServeGFSFile)))
	RunHTTPServer()
}
<commit_msg>Serve /farm/ from the web app.<commit_after>// stardew.rocks web server
//
// It contains a few HTTP handlers:
// - a static handler that serves files from /home/stardew/www.
// - a handler that serves the latest complete file save in XML format.
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/nictuku/stardew-rocks/stardb"
)

func logHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL)
		handler.ServeHTTP(w, r)
	})
}

func wwwDir() string {
	home := os.Getenv("HOME")
	if home == "" {
		home = string(filepath.Separator)
	}
	return filepath.Clean(filepath.Join(home, "www"))
}

func Index(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, filepath.Join(wwwDir(), "index.html"))
}

func GetFarms(w http.ResponseWriter, r *http.Request) {
	b, err := stardb.FarmsJSON()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	_, err = w.Write(b)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	return
}

func GetFarm(w http.ResponseWriter, r *http.Request) {
	b, err := stardb.FarmsJSON()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	_, err = w.Write(b)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	return
}

func ServeGFSFile(w http.ResponseWriter, r *http.Request) {
	if !strings.HasPrefix(r.URL.Path, "/screenshot") {
		http.Error(w, "Not found"+r.URL.Path, http.StatusNotFound)
		return
	}
	s := strings.Split(r.URL.Path, "/")
	if len(s) != 4 {
		http.Error(w, fmt.Sprintf("...%v - %d", s, len(s)), http.StatusNotFound)
		return
	}
	if s[1] != "screenshot" && s[1] != "saveGames" {
		http.Error(w, "no.. Not found", http.StatusNotFound)
		return
	}

	f, err := stardb.GFS.Open(r.URL.Path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	_, err = io.Copy(w, f)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	return
}

func RunHTTPServer() {
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Println("Error starting www server:", err)
		// os.IsPermission doesn't match.
		if strings.Contains(err.Error(), "permission denied") {
			// Only for linux and port 80. Keeping here for future convenience.
			log.Println("Try: sudo setcap 'cap_net_bind_service=+ep' www")
		}
	}
}

func main() {
	dir := wwwDir()
	log.Printf("Serving files from %v", dir)
	http.Handle("/api/farms", logHandler(http.HandlerFunc(GetFarms)))
	http.Handle("/screenshot/", logHandler(http.HandlerFunc(ServeGFSFile)))
	http.Handle("/saveGames/", logHandler(http.HandlerFunc(ServeGFSFile)))

	// This is served by the web app.
	http.Handle("/farm/", logHandler(http.HandlerFunc(Index)))

	// This is served from the filesystem, but / goes to index.html which has our web app.
	http.Handle("/", logHandler(http.FileServer(http.Dir(dir))))
	RunHTTPServer()
}
<|endoftext|>


<commit_before>// Copyright (c) - Damien Fontaine <damien.fontaine@lineolia.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>

package web

import (
	"crypto/tls"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/Sirupsen/logrus/hooks/test"

	"golang.org/x/net/http2"
)

func getHTTPServer(t *testing.T, env string) (s *Server) {
	s, err := NewServer("config.yml", env)
	if err != nil {
		t.Fatalf("Non expected error: %v", err)
	}
	m := s.Handler.(*LoggingServeMux)
	m.Handle("/", SingleFile("hello.html"))
	return
}

func TestNewWebServerWithNoLog(t *testing.T) {
	server := getHTTPServer(t, "testNoLog")

	if server.Config.Port != 8888 {
		t.Fatalf("Non expected server port: %v != %v", 8888, server.Config.Port)
	}
	if server.Config.Jwt.Key != "LunarcSecretKey" {
		t.Fatalf("Non expected server Jwt secret key: %v != %v", "LunarcSecretKey", server.Config.Jwt.Key)
	}
}

func TestNewWebServer(t *testing.T) {
	server := getHTTPServer(t, "test")

	if server.Config.Port != 8888 {
		t.Fatalf("Non expected server port: %v != %v", 8888, server.Config.Port)
	}
	if server.Config.Jwt.Key != "LunarcSecretKey" {
		t.Fatalf("Non expected server Jwt secret key: %v != %v", "LunarcSecretKey", server.Config.Jwt.Key)
	}
}

func TestStart(t *testing.T) {
	server := getHTTPServer(t, "test")

	go server.Start()

	time.Sleep(time.Second * 3)

	resp, err := http.Get("http://localhost:8888/")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if !strings.Contains(string(body), "Lunarc") {
		t.Fatalf("Body must contain Lunarc word but : %v", body)
	}

	go server.Stop()
	<-server.Done
}

func TestStartWithSSLNormal(t *testing.T) {
	server := getHTTPServer(t, "ssl")

	go server.Start()

	time.Sleep(time.Second * 3)

	client := &http.Client{Transport: &http2.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true, NextProtos: []string{"h2"}}}}

	response, err := client.Get("https://localhost:8888/")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if response.TLS == nil {
		t.Fatalf("This connection must be in HTTPS")
	}

	if strings.Compare(response.Proto, "HTTP/2.0") != 0 {
		t.Fatalf("Must use HTTP/2 but use : %v", response.Proto)
	}

	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if !strings.Contains(string(body), "Lunarc") {
		t.Fatalf("Body must contain Lunarc word but : %v", body)
	}

	go server.Stop()
	<-server.Done
}

func TestStartWithSSLNoCertError(t *testing.T) {
	server := getHTTPServer(t, "nocertssl")

	go server.Start()

	err := <-server.Error
	if err == nil {
		t.Fatalf("Expected error: Le fichier spécifié est introuvable")
	}
}

func TestStartWithSSLNoKeyError(t *testing.T) {
	server := getHTTPServer(t, "nokeyssl")

	go server.Start()

	err := <-server.Error
	if err == nil {
		t.Fatalf("Expected error: Le fichier spécifié est introuvable")
	}
}

func TestStartWithError(t *testing.T) {
	l, err := net.Listen("tcp", ":8888")
	if err != nil {
		t.Fatalf("Error during test preparation : %v", err)
	}

	done := make(chan struct{}, 1)

	go func() {
		err = http.Serve(l, nil)
		if err != nil {
			close(done)
		}
	}()

	server := getHTTPServer(t, "test")

	go server.Start()

	err = <-server.Error

	if err == nil {
		t.Fatalf("Expected error: listen tcp :8888: bind: address already in use")
	}
	l.Close()
	<-done
}

func TestStartWithSSLAndError(t *testing.T) {
	l, err := net.Listen("tcp", ":8888")
	if err != nil {
		t.Fatalf("Error during test preparation : %v", err)
	}

	done := make(chan struct{}, 1)

	go func() {
		err = http.Serve(l, nil)
		if err != nil {
			close(done)
		}
	}()

	server := getHTTPServer(t, "ssl")

	go server.Start()

	err = <-server.Error

	if err == nil {
		t.Fatalf("Expected error: listen tcp :8888: bind: address already in use")
	}

	l.Close()
	<-done
}

func TestStopNormal(t *testing.T) {
	server := getHTTPServer(t, "test")

	go server.Start()

	time.Sleep(time.Second * 2)

	resp, err := http.Get("http://localhost:8888/")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if !strings.Contains(string(body), "Lunarc") {
		t.Fatalf("Body must contain Lunarc word but : %v", body)
	}

	go server.Stop()
	<-server.Done

	resp, err = http.Get("http://localhost:8888/")
	if err == nil {
		t.Fatalf("Error expected: Not Found: %v", resp)
	}

	l, err := net.Listen("tcp", ":8888")
	if err != nil {
		t.Fatalf("Error : %v", err)
	}
	defer l.Close()
	go http.Serve(l, nil)
}

func TestStopUnstarted(t *testing.T) {
	server := getHTTPServer(t, "test")

	go server.Stop()

	select {
	case result := <-server.Done:
		if result {
			t.Fatalf("Non expected behavior")
		}
	case err := <-server.Error:
		if err == nil {
			t.Fatalf("Non expected error")
		}
		return
	}
}

func GetLoggingHTTPServer(t *testing.T, env string) (s *Server) {
	s, err := NewServer("config.yml", env)
	if err != nil {
		t.Fatalf("Non expected error: %v", err)
	}
	return
}

func TestNewLoggingServeMux(t *testing.T) {
	server := GetLoggingHTTPServer(t, "test")

	go server.Start()

	hook := test.NewGlobal()

	_, err := http.Get("http://localhost:8888/")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if len(hook.Entries) != 1 {
		t.Fatalf("Must return 1 but : %d", len(hook.Entries))
	}

	go server.Stop()
	<-server.Done
}
<commit_msg>Rename test functions<commit_after>// Copyright (c) - Damien Fontaine <damien.fontaine@lineolia.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>

package web

import (
	"crypto/tls"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/Sirupsen/logrus/hooks/test"

	"golang.org/x/net/http2"
)

func getHTTPServer(t *testing.T, env string) (s *Server) {
	s, err := NewServer("config.yml", env)
	if err != nil {
		t.Fatalf("Non expected error: %v", err)
	}
	m := s.Handler.(*LoggingServeMux)
	m.Handle("/", SingleFile("hello.html"))
	return
}

func TestNewServerWithNoLog(t *testing.T) {
	server := getHTTPServer(t, "testNoLog")

	if server.Config.Port != 8888 {
		t.Fatalf("Non expected server port: %v != %v", 8888, server.Config.Port)
	}
	if server.Config.Jwt.Key != "LunarcSecretKey" {
		t.Fatalf("Non expected server Jwt secret key: %v != %v", "LunarcSecretKey", server.Config.Jwt.Key)
	}
}

func TestNewServer(t *testing.T) {
	server := getHTTPServer(t, "test")

	if server.Config.Port != 8888 {
		t.Fatalf("Non expected server port: %v != %v", 8888, server.Config.Port)
	}
	if server.Config.Jwt.Key != "LunarcSecretKey" {
		t.Fatalf("Non expected server Jwt secret key: %v != %v", "LunarcSecretKey", server.Config.Jwt.Key)
	}
}

func TestStart(t *testing.T) {
	server := getHTTPServer(t, "test")

	go server.Start()

	time.Sleep(time.Second * 3)

	resp, err := http.Get("http://localhost:8888/")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if !strings.Contains(string(body), "Lunarc") {
		t.Fatalf("Body must contain Lunarc word but : %v", body)
	}

	go server.Stop()
	<-server.Done
}

func TestStartWithSSLNormal(t *testing.T) {
	server := getHTTPServer(t, "ssl")

	go server.Start()

	time.Sleep(time.Second * 3)

	client := &http.Client{Transport: &http2.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true, NextProtos: []string{"h2"}}}}

	response, err := client.Get("https://localhost:8888/")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if response.TLS == nil {
		t.Fatalf("This connection must be in HTTPS")
	}

	if strings.Compare(response.Proto, "HTTP/2.0") != 0 {
		t.Fatalf("Must use HTTP/2 but use : %v", response.Proto)
	}

	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if !strings.Contains(string(body), "Lunarc") {
		t.Fatalf("Body must contain Lunarc word but : %v", body)
	}

	go server.Stop()
	<-server.Done
}

func TestStartWithSSLNoCertError(t *testing.T) {
	server := getHTTPServer(t, "nocertssl")

	go server.Start()

	err := <-server.Error
	if err == nil {
		t.Fatalf("Expected error: Le fichier spécifié est introuvable")
	}
}

func TestStartWithSSLNoKeyError(t *testing.T) {
	server := getHTTPServer(t, "nokeyssl")

	go server.Start()

	err := <-server.Error
	if err == nil {
		t.Fatalf("Expected error: Le fichier spécifié est introuvable")
	}
}

func TestStartWithError(t *testing.T) {
	l, err := net.Listen("tcp", ":8888")
	if err != nil {
		t.Fatalf("Error during test preparation : %v", err)
	}

	done := make(chan struct{}, 1)

	go func() {
		err = http.Serve(l, nil)
		if err != nil {
			close(done)
		}
	}()

	server := getHTTPServer(t, "test")

	go server.Start()

	err = <-server.Error

	if err == nil {
		t.Fatalf("Expected error: listen tcp :8888: bind: address already in use")
	}
	l.Close()
	<-done
}

func TestStartWithSSLAndError(t *testing.T) {
	l, err := net.Listen("tcp", ":8888")
	if err != nil {
		t.Fatalf("Error during test preparation : %v", err)
	}

	done := make(chan struct{}, 1)

	go func() {
		err = http.Serve(l, nil)
		if err != nil {
			close(done)
		}
	}()

	server := getHTTPServer(t, "ssl")

	go server.Start()

	err = <-server.Error

	if err == nil {
		t.Fatalf("Expected error: listen tcp :8888: bind: address already in use")
	}

	l.Close()
	<-done
}

func TestStopNormal(t *testing.T) {
	server := getHTTPServer(t, "test")

	go server.Start()

	time.Sleep(time.Second * 2)

	resp, err := http.Get("http://localhost:8888/")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if !strings.Contains(string(body), "Lunarc") {
		t.Fatalf("Body must contain Lunarc word but : %v", body)
	}

	go server.Stop()
	<-server.Done

	resp, err = http.Get("http://localhost:8888/")
	if err == nil {
		t.Fatalf("Error expected: Not Found: %v", resp)
	}

	l, err := net.Listen("tcp", ":8888")
	if err != nil {
		t.Fatalf("Error : %v", err)
	}
	defer l.Close()
	go http.Serve(l, nil)
}

func TestStopUnstarted(t *testing.T) {
	server := getHTTPServer(t, "test")

	go server.Stop()

	select {
	case result := <-server.Done:
		if result {
			t.Fatalf("Non expected behavior")
		}
	case err := <-server.Error:
		if err == nil {
			t.Fatalf("Non expected error")
		}
		return
	}
}

func GetLoggingHTTPServer(t *testing.T, env string) (s *Server) {
	s, err := NewServer("config.yml", env)
	if err != nil {
		t.Fatalf("Non expected error: %v", err)
	}
	return
}

func TestNewLoggingServeMux(t *testing.T) {
	server := GetLoggingHTTPServer(t, "test")

	go server.Start()

	hook := test.NewGlobal()

	_, err := http.Get("http://localhost:8888/")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if len(hook.Entries) != 1 {
		t.Fatalf("Must return 1 but : %d", len(hook.Entries))
	}

	go server.Stop()
	<-server.Done
}
<|endoftext|>


<commit_before>package webapi

import (
	"fmt"
	"github.com/zmb3/spotify"
	"os"
	"bufio"
	"strings"
	"golang.org/x/oauth2"
	"strconv"
	"time"
)

func Auth(clientId string, authRedirectUrl string) *spotify.Client {
	if clientId == "" {
		fmt.Print("Spotify Client ID not set")
		return nil
	}

	auth := spotify.NewAuthenticator(authRedirectUrl,
		spotify.ScopeUserLibraryRead,
		spotify.ScopeUserFollowRead,
		spotify.ScopePlaylistReadCollaborative,
		spotify.ScopePlaylistReadPrivate)

	auth.SetAuthInfo(clientId, "")

	url := auth.AuthURL("")
	url = strings.Replace(url, "response_type=code", "response_type=token", -1)
	fmt.Printf("Go to url %v\n", url)

	fmt.Print("And paste the access token here: ")
	reader := bufio.NewReader(os.Stdin)
	accessToken, _ := reader.ReadString('\n')
	accessToken = strings.Trim(accessToken, " \n\r")

	result := strings.Split(accessToken, " ")

	seconds, err := strconv.ParseInt(strings.Split(result[2], ":")[1], 10, 64)
	if err != nil {
		fmt.Printf("Error %v\n", err)
		return nil
	}
	expiry := time.Now().Add(time.Duration(seconds) * time.Second)
	token := &oauth2.Token{
		AccessToken:  strings.Split(result[0], ":")[1],
		TokenType:    strings.Split(result[1], ":")[1],
		Expiry:       expiry,
	}

	client := auth.NewClient(token)
	return &client
}

<commit_msg>Add spaces around web auth url<commit_after>package webapi

import (
	"fmt"
	"github.com/zmb3/spotify"
	"os"
	"bufio"
	"strings"
	"golang.org/x/oauth2"
	"strconv"
	"time"
)

func Auth(clientId string, authRedirectUrl string) *spotify.Client {
	if clientId == "" {
		fmt.Print("Spotify Client ID not set")
		return nil
	}

	auth := spotify.NewAuthenticator(authRedirectUrl,
		spotify.ScopeUserLibraryRead,
		spotify.ScopeUserFollowRead,
		spotify.ScopePlaylistReadCollaborative,
		spotify.ScopePlaylistReadPrivate)

	auth.SetAuthInfo(clientId, "")

	url := auth.AuthURL("")
	url = strings.Replace(url, "response_type=code", "response_type=token", -1)
	fmt.Printf("For web api authorization go to url:\n\n%v\n\n", url)

	fmt.Print("And paste the access token here: ")
	reader := bufio.NewReader(os.Stdin)
	accessToken, _ := reader.ReadString('\n')
	accessToken = strings.Trim(accessToken, " \n\r")

	result := strings.Split(accessToken, " ")

	seconds, err := strconv.ParseInt(strings.Split(result[2], ":")[1], 10, 64)
	if err != nil {
		fmt.Printf("Error %v\n", err)
		return nil
	}
	expiry := time.Now().Add(time.Duration(seconds) * time.Second)
	token := &oauth2.Token{
		AccessToken:  strings.Split(result[0], ":")[1],
		TokenType:    strings.Split(result[1], ":")[1],
		Expiry:       expiry,
	}

	client := auth.NewClient(token)
	return &client
}

<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package fieldmanager

import (
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
	openapiproto "k8s.io/kube-openapi/pkg/util/proto"
	"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
)

// DefaultMaxUpdateManagers defines the default maximum retained number of managedFields entries from updates
// if the number of update managers exceeds this, the oldest entries will be merged until the number is below the maximum.
// TODO(jennybuckley): Determine if this is really the best value. Ideally we wouldn't unnecessarily merge too many entries.
const DefaultMaxUpdateManagers int = 10

// DefaultTrackOnCreateProbability defines the default probability that the field management of an object
// starts being tracked from the object's creation, instead of from the first time the object is applied to.
const DefaultTrackOnCreateProbability float32 = 0.0

// Managed groups a fieldpath.ManagedFields together with the timestamps associated with each operation.
type Managed interface {
	// Fields gets the fieldpath.ManagedFields.
	Fields() fieldpath.ManagedFields

	// Times gets the timestamps associated with each operation.
	Times() map[string]*metav1.Time
}

// Manager updates the managed fields and merges applied configurations.
type Manager interface {
	// Update is used when the object has already been merged (non-apply
	// use-case), and simply updates the managed fields in the output
	// object.
	Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error)

	// Apply is used when server-side apply is called, as it merges the
	// object and updates the managed fields.
	Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error)
}

// FieldManager updates the managed fields and merge applied
// configurations.
type FieldManager struct {
	fieldManager Manager
}

// NewFieldManager creates a new FieldManager that decodes, manages, then re-encodes managedFields
// on update and apply requests.
func NewFieldManager(f Manager) *FieldManager {
	return &FieldManager{f}
}

// NewDefaultFieldManager creates a new FieldManager that merges apply requests
// and update managed fields for other types of requests.
func NewDefaultFieldManager(models openapiproto.Models, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion) (*FieldManager, error) {
	f, err := NewStructuredMergeManager(models, objectConverter, objectDefaulter, kind.GroupVersion(), hub)
	if err != nil {
		return nil, fmt.Errorf("failed to create field manager: %v", err)
	}
	return newDefaultFieldManager(f, objectCreater, kind), nil
}

// NewDefaultCRDFieldManager creates a new FieldManager specifically for
// CRDs. This allows for the possibility of fields which are not defined
// in models, as well as having no models defined at all.
func NewDefaultCRDFieldManager(models openapiproto.Models, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, preserveUnknownFields bool) (_ *FieldManager, err error) {
	f, err := NewCRDStructuredMergeManager(models, objectConverter, objectDefaulter, kind.GroupVersion(), hub, preserveUnknownFields)
	if err != nil {
		return nil, fmt.Errorf("failed to create field manager: %v", err)
	}
	return newDefaultFieldManager(f, objectCreater, kind), nil
}

// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
func newDefaultFieldManager(f Manager, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind) *FieldManager {
	f = NewStripMetaManager(f)
	f = NewBuildManagerInfoManager(f, kind.GroupVersion())
	f = NewCapManagersManager(f, DefaultMaxUpdateManagers)
	f = NewProbabilisticSkipNonAppliedManager(f, objectCreater, kind, DefaultTrackOnCreateProbability)
	return NewFieldManager(f)
}

// Update is used when the object has already been merged (non-apply
// use-case), and simply updates the managed fields in the output
// object.
func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (object runtime.Object, err error) {
	// If the object doesn't have metadata, we should just return without trying to
	// set the managedFields at all, so creates/updates/patches will work normally.
	if _, err = meta.Accessor(newObj); err != nil {
		return newObj, nil
	}

	// First try to decode the managed fields provided in the update,
	// This is necessary to allow directly updating managed fields.
	var managed Managed
	if managed, err = internal.DecodeObjectManagedFields(newObj); err != nil || len(managed.Fields()) == 0 {
		// If the managed field is empty or we failed to decode it,
		// let's try the live object. This is to prevent clients who
		// don't understand managedFields from deleting it accidentally.
		managed, err = internal.DecodeObjectManagedFields(liveObj)
		if err != nil {
			return nil, fmt.Errorf("failed to decode managed fields: %v", err)
		}
	}

	internal.RemoveObjectManagedFields(liveObj)
	internal.RemoveObjectManagedFields(newObj)

	if object, managed, err = f.fieldManager.Update(liveObj, newObj, managed, manager); err != nil {
		return nil, err
	}

	if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
		return nil, fmt.Errorf("failed to encode managed fields: %v", err)
	}

	return object, nil
}

// Apply is used when server-side apply is called, as it merges the
// object and updates the managed fields.
func (f *FieldManager) Apply(liveObj, appliedObj runtime.Object, manager string, force bool) (object runtime.Object, err error) {
	// If the object doesn't have metadata, apply isn't allowed.
	if _, err = meta.Accessor(liveObj); err != nil {
		return nil, fmt.Errorf("couldn't get accessor: %v", err)
	}

	// Decode the managed fields in the live object, since it isn't allowed in the patch.
	var managed Managed
	if managed, err = internal.DecodeObjectManagedFields(liveObj); err != nil {
		return nil, fmt.Errorf("failed to decode managed fields: %v", err)
	}

	internal.RemoveObjectManagedFields(liveObj)

	if object, managed, err = f.fieldManager.Apply(liveObj, appliedObj, managed, manager, force); err != nil {
		return nil, err
	}

	if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
		return nil, fmt.Errorf("failed to encode managed fields: %v", err)
	}

	return object, nil
}
<commit_msg>Re-enable apply for 50% of requests<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package fieldmanager

import (
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
	openapiproto "k8s.io/kube-openapi/pkg/util/proto"
	"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
)

// DefaultMaxUpdateManagers defines the default maximum retained number of managedFields entries from updates
// if the number of update managers exceeds this, the oldest entries will be merged until the number is below the maximum.
// TODO(jennybuckley): Determine if this is really the best value. Ideally we wouldn't unnecessarily merge too many entries.
const DefaultMaxUpdateManagers int = 10

// DefaultTrackOnCreateProbability defines the default probability that the field management of an object
// starts being tracked from the object's creation, instead of from the first time the object is applied to.
const DefaultTrackOnCreateProbability float32 = 0.5

// Managed groups a fieldpath.ManagedFields together with the timestamps associated with each operation.
type Managed interface {
	// Fields gets the fieldpath.ManagedFields.
	Fields() fieldpath.ManagedFields

	// Times gets the timestamps associated with each operation.
	Times() map[string]*metav1.Time
}

// Manager updates the managed fields and merges applied configurations.
type Manager interface {
	// Update is used when the object has already been merged (non-apply
	// use-case), and simply updates the managed fields in the output
	// object.
	Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error)

	// Apply is used when server-side apply is called, as it merges the
	// object and updates the managed fields.
	Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error)
}

// FieldManager updates the managed fields and merge applied
// configurations.
type FieldManager struct {
	fieldManager Manager
}

// NewFieldManager creates a new FieldManager that decodes, manages, then re-encodes managedFields
// on update and apply requests.
func NewFieldManager(f Manager) *FieldManager {
	return &FieldManager{f}
}

// NewDefaultFieldManager creates a new FieldManager that merges apply requests
// and update managed fields for other types of requests.
func NewDefaultFieldManager(models openapiproto.Models, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion) (*FieldManager, error) {
	f, err := NewStructuredMergeManager(models, objectConverter, objectDefaulter, kind.GroupVersion(), hub)
	if err != nil {
		return nil, fmt.Errorf("failed to create field manager: %v", err)
	}
	return newDefaultFieldManager(f, objectCreater, kind), nil
}

// NewDefaultCRDFieldManager creates a new FieldManager specifically for
// CRDs. This allows for the possibility of fields which are not defined
// in models, as well as having no models defined at all.
func NewDefaultCRDFieldManager(models openapiproto.Models, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, preserveUnknownFields bool) (_ *FieldManager, err error) {
	f, err := NewCRDStructuredMergeManager(models, objectConverter, objectDefaulter, kind.GroupVersion(), hub, preserveUnknownFields)
	if err != nil {
		return nil, fmt.Errorf("failed to create field manager: %v", err)
	}
	return newDefaultFieldManager(f, objectCreater, kind), nil
}

// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
func newDefaultFieldManager(f Manager, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind) *FieldManager {
	f = NewStripMetaManager(f)
	f = NewBuildManagerInfoManager(f, kind.GroupVersion())
	f = NewCapManagersManager(f, DefaultMaxUpdateManagers)
	f = NewProbabilisticSkipNonAppliedManager(f, objectCreater, kind, DefaultTrackOnCreateProbability)
	return NewFieldManager(f)
}

// Update is used when the object has already been merged (non-apply
// use-case), and simply updates the managed fields in the output
// object.
func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (object runtime.Object, err error) {
	// If the object doesn't have metadata, we should just return without trying to
	// set the managedFields at all, so creates/updates/patches will work normally.
	if _, err = meta.Accessor(newObj); err != nil {
		return newObj, nil
	}

	// First try to decode the managed fields provided in the update,
	// This is necessary to allow directly updating managed fields.
	var managed Managed
	if managed, err = internal.DecodeObjectManagedFields(newObj); err != nil || len(managed.Fields()) == 0 {
		// If the managed field is empty or we failed to decode it,
		// let's try the live object. This is to prevent clients who
		// don't understand managedFields from deleting it accidentally.
		managed, err = internal.DecodeObjectManagedFields(liveObj)
		if err != nil {
			return nil, fmt.Errorf("failed to decode managed fields: %v", err)
		}
	}

	internal.RemoveObjectManagedFields(liveObj)
	internal.RemoveObjectManagedFields(newObj)

	if object, managed, err = f.fieldManager.Update(liveObj, newObj, managed, manager); err != nil {
		return nil, err
	}

	if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
		return nil, fmt.Errorf("failed to encode managed fields: %v", err)
	}

	return object, nil
}

// Apply is used when server-side apply is called, as it merges the
// object and updates the managed fields.
func (f *FieldManager) Apply(liveObj, appliedObj runtime.Object, manager string, force bool) (object runtime.Object, err error) {
	// If the object doesn't have metadata, apply isn't allowed.
	if _, err = meta.Accessor(liveObj); err != nil {
		return nil, fmt.Errorf("couldn't get accessor: %v", err)
	}

	// Decode the managed fields in the live object, since it isn't allowed in the patch.
	var managed Managed
	if managed, err = internal.DecodeObjectManagedFields(liveObj); err != nil {
		return nil, fmt.Errorf("failed to decode managed fields: %v", err)
	}

	internal.RemoveObjectManagedFields(liveObj)

	if object, managed, err = f.fieldManager.Apply(liveObj, appliedObj, managed, manager, force); err != nil {
		return nil, err
	}

	if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
		return nil, fmt.Errorf("failed to encode managed fields: %v", err)
	}

	return object, nil
}
<|endoftext|>