<commit_before>package ironman

import (
	"log"
	"path/filepath"

	"github.com/ironman-project/ironman/template/validator"

	"github.com/blevesearch/bleve"
	"github.com/ironman-project/ironman/template/manager"
	"github.com/ironman-project/ironman/template/manager/git"
	"github.com/ironman-project/ironman/template/model"
	"github.com/ironman-project/ironman/template/repository"
	brepository "github.com/ironman-project/ironman/template/repository/bleve"
	"github.com/pkg/errors"
)

const (
	indexName = "templates.index"
)

//Ironman is the one administering the local
type Ironman struct {
	manager     manager.Manager
	modelReader model.Reader
	repository  repository.Repository
	home        string
	validators  []validator.Validator
}

//New returns a new instance of ironman
func New(home string, options ...Option) *Ironman {
	ir := &Ironman{}
	for _, option := range options {
		option(ir)
	}
	if ir.manager == nil {
		manager := git.New(home)
		ir.manager = manager
	}

	if ir.repository == nil {
		indexPath := filepath.Join(home, indexName)
		index, err := buildIndex(indexPath)
		if err != nil {
			log.Fatal("Failed to create ironman templates index", err)
		}
		ir.repository = brepository.New(
			brepository.SetIndex(index),
		)
	}

	if ir.modelReader == nil {
		decoder := model.NewDecoder(model.DecoderTypeYAML)
		modelReader := model.NewFSReader([]string{".git"}, model.MetadataFileExtensionYAML, decoder)
		ir.modelReader = modelReader
	}

	if ir.validators == nil {
		ir.validators = []validator.Validator{}
	}
	return ir
}

func buildIndex(path string) (bleve.Index, error) {
	// open the index
	index, err := bleve.Open(path)
	if err == bleve.ErrorIndexPathDoesNotExist {
		index, err = brepository.BuildIndex(path)
		if err != nil {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	}
	return index, nil
}

//Install installs a new template based on a template locator
func (i *Ironman) Install(templateLocator string) error {

	ID, err := i.manager.Install(templateLocator)

	if err != nil {
		return err
	}

	templatePath := i.manager.TemplateLocation(ID)
	model, err := i.modelReader.Read(templatePath)

	if err != nil {
		return err
	}

	_, err = i.repository.Index(model)

	if err != nil {
		//rollback manager installation
		_ = i.manager.Uninstall(ID)
		return err
	}

	return nil
}

//Link Creates a symlink to the ironman repository from any path in the filesystem
func (i *Ironman) Link(templatePath, templateID string) error {

	err := i.manager.Link(templatePath, templateID)

	if err != nil {
		return err
	}

	return nil
}

//List returns a list of all the installed ironman templates
func (i *Ironman) List() ([]*model.Template, error) {
	results, err := i.repository.List()
	if err != nil {
		return nil, err
	}

	return results, nil
}

//Uninstall uninstalls an ironman template
func (i *Ironman) Uninstall(templateID string) error {

	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	err = i.manager.Uninstall(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Unlink unlinks a previously linked ironman template
func (i *Ironman) Unlink(templateID string) error {

	err := i.manager.Unlink(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Update updates an iroman template
func (i *Ironman) Update(templateID string) error {
	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	err = i.manager.Update(templateID)

	if err != nil {
		return err
	}

	return nil
}
<commit_msg>create model validation code skeleton<commit_after>package ironman

import (
	"bytes"
	"log"
	"path/filepath"
	"text/template"

	"github.com/ironman-project/ironman/template/validator"

	"github.com/blevesearch/bleve"
	"github.com/ironman-project/ironman/template/manager"
	"github.com/ironman-project/ironman/template/manager/git"
	"github.com/ironman-project/ironman/template/model"
	"github.com/ironman-project/ironman/template/repository"
	brepository "github.com/ironman-project/ironman/template/repository/bleve"
	"github.com/pkg/errors"
)

const (
	indexName = "templates.index"
)

var validationTempl *template.Template

const validatoinTemplateText = ``

func init() {
	var err error
	validationTempl, err = template.New("validationTemplate").Parse(validatoinTemplateText)
	if err != nil {
		log.Fatalf("Failed to initialize validation errors template %s", err)
	}
}

//Ironman is the one administering the local
type Ironman struct {
	manager     manager.Manager
	modelReader model.Reader
	repository  repository.Repository
	home        string
	validators  []validator.Validator
}

//New returns a new instance of ironman
func New(home string, options ...Option) *Ironman {
	ir := &Ironman{}
	for _, option := range options {
		option(ir)
	}
	if ir.manager == nil {
		manager := git.New(home)
		ir.manager = manager
	}

	if ir.repository == nil {
		indexPath := filepath.Join(home, indexName)
		index, err := buildIndex(indexPath)
		if err != nil {
			log.Fatal("Failed to create ironman templates index", err)
		}
		ir.repository = brepository.New(
			brepository.SetIndex(index),
		)
	}

	if ir.modelReader == nil {
		decoder := model.NewDecoder(model.DecoderTypeYAML)
		modelReader := model.NewFSReader([]string{".git"}, model.MetadataFileExtensionYAML, decoder)
		ir.modelReader = modelReader
	}

	if ir.validators == nil {
		ir.validators = []validator.Validator{}
	}
	return ir
}

func buildIndex(path string) (bleve.Index, error) {
	// open the index
	index, err := bleve.Open(path)
	if err == bleve.ErrorIndexPathDoesNotExist {
		index, err = brepository.BuildIndex(path)
		if err != nil {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	}
	return index, nil
}

//Install installs a new template based on a template locator
func (i *Ironman) Install(templateLocator string) error {

	ID, err := i.manager.Install(templateLocator)

	if err != nil {
		return err
	}

	templatePath := i.manager.TemplateLocation(ID)
	model, err := i.modelReader.Read(templatePath)

	if err != nil {
		return err
	}

	//validate model
	for _, validator := range i.validators {
		valid, validationErr, err := validator.Validate(model)

		if err != nil {
			return errors.Wrap(err, "Failed to validate model")
		}

		if !valid {
			var validationErrBuffer bytes.Buffer
			err := validationTempl.Execute(&validationErrBuffer, validationErr)

			if err != nil {
				return errors.Wrap(err, "Failed to create validation error message")
			}

			return errors.New(validationErrBuffer.String())
		}
	}

	_, err = i.repository.Index(model)

	if err != nil {
		//rollback manager installation
		_ = i.manager.Uninstall(ID)
		return err
	}

	return nil
}

//Link Creates a symlink to the ironman repository from any path in the filesystem
func (i *Ironman) Link(templatePath, templateID string) error {

	err := i.manager.Link(templatePath, templateID)

	if err != nil {
		return err
	}

	return nil
}

//List returns a list of all the installed ironman templates
func (i *Ironman) List() ([]*model.Template, error) {
	results, err := i.repository.List()
	if err != nil {
		return nil, err
	}

	return results, nil
}

//Uninstall uninstalls an ironman template
func (i *Ironman) Uninstall(templateID string) error {

	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	err = i.manager.Uninstall(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Unlink unlinks a previously linked ironman template
func (i *Ironman) Unlink(templateID string) error {

	err := i.manager.Unlink(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Update updates an iroman template
func (i *Ironman) Update(templateID string) error {
	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	err = i.manager.Update(templateID)

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

// TODO -q, --quiet for each command (own log that can be set to quiet mode)
// TODO docs for <dir>, --rebuild, --skeleton
const usage = `
LitePub - a lightweight static blog generator, http://litepub.com

Usage:
  litepub create [<dir>] [-s, --skeleton]
  litepub build  [<dir>]
  litepub serve  [<dir>] [-R, --rebuild] [-p, --port <port>] [-w, --watch]

Arguments:
  <dir>  The directory to create the blog in or look for; it will be created if
         it doesn't exist (only when creating a blog) [default: .]

Options:
  -s, --skeleton     Don't create sample posts and templates
  -R, --rebuild      Rebuild the blog before serving
  -p, --port <port>  The port to listen on [default: 2703]
  -w, --watch        Rebuild the blog when posts or templates change
  -h, --help         Show this screen
  -v, --version      Show version
`
<commit_msg>Removed a TODO<commit_after>package main

// TODO -q, --quiet for each command (own log that can be set to quiet mode)
const usage = `
LitePub - a lightweight static blog generator, http://litepub.com

Usage:
  litepub create [<dir>] [-s, --skeleton]
  litepub build  [<dir>]
  litepub serve  [<dir>] [-R, --rebuild] [-p, --port <port>] [-w, --watch]

Arguments:
  <dir>  The directory to create the blog in or look for; it will be created if
         it doesn't exist (only when creating a blog) [default: .]

Options:
  -s, --skeleton     Don't create sample posts and templates
  -R, --rebuild      Rebuild the blog before serving
  -p, --port <port>  The port to listen on [default: 2703]
  -w, --watch        Rebuild the blog when posts or templates change
  -h, --help         Show this screen
  -v, --version      Show version
`
<|endoftext|>


<commit_before>// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or 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 constants

// Version number string.
const Version = "0.2.15"
<commit_msg>Version 0.2.16<commit_after>// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or 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 constants

// Version number string.
const Version = "0.2.16"
<|endoftext|>


<commit_before>package masterapi

import (
	"encoding/json"
	"fmt"
	"github.com/KIT-MAMID/mamid/model"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"github.com/mattn/go-sqlite3"
	"log"
	"net/http"
	"strconv"
)

type Slave struct {
	ID                   uint   `json:"id"`
	Hostname             string `json:"hostname"`
	Port                 uint   `json:"slave_port"`
	MongodPortRangeBegin uint   `json:"mongod_port_range_begin"` //inclusive
	MongodPortRangeEnd   uint   `json:"mongod_port_range_end"`   //exclusive
	PersistentStorage    bool   `json:"persistent_storage"`
	ConfiguredState      string `json:"configured_state"`
	RiskGroupID          uint   `json:"risk_group_id"`
}

func (m *MasterAPI) SlaveIndex(w http.ResponseWriter, r *http.Request) {

	var slaves []*model.Slave
	err := m.DB.Order("id", false).Find(&slaves).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	out := make([]*Slave, len(slaves))
	for i, v := range slaves {
		out[i] = ProjectModelSlaveToSlave(v)
	}
	json.NewEncoder(w).Encode(out)
}

func (m *MasterAPI) SlaveById(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	if id == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "id may not be 0")
		return
	}

	var slave model.Slave
	res := m.DB.First(&slave, id)

	if res.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if err = res.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(&slave))
	return
}

func (m *MasterAPI) SlavePut(w http.ResponseWriter, r *http.Request) {
	var postSlave Slave
	err := json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not specify the slave ID in PUT request")
		return
	}

	modelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	}

	// Persist to database

	tx := m.DB.Begin()

	err = tx.Create(&modelSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok && driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, driverErr.Error())
		tx.Rollback()
		return
	}

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

	// Return created slave

	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(modelSlave))

	return
}

func (m *MasterAPI) SlaveUpdate(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	var postSlave Slave
	err = json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != id {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not change the id of an object")
		return
	}

	if err = postSlave.assertNoZeroFieldsSet(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not POST JSON with zero values in any field: %s", err.Error())
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	tx := m.DB.Begin()

	var modelSlave model.Slave
	modelSlaveRes := tx.First(&modelSlave, id)
	if modelSlaveRes.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	} else if err = modelSlaveRes.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	updatedModelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	permissionError, dbError := changeToSlaveAllowed(tx, &modelSlave, updatedModelSlave)
	if dbError != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, dbError)
		tx.Rollback()
		return
	}
	if permissionError != nil {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprint(w, permissionError)
		tx.Rollback()
		return
	}

	// Persist to database
	err = tx.Save(&updatedModelSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok {
		if driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprint(w, driverErr.Error())
			tx.Rollback()
			return
		}
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

}

func (m *MasterAPI) SlaveDelete(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	tx := m.DB.Begin()

	// Can only delete disabled slaves
	var currentSlave model.Slave
	if err = tx.First(&currentSlave, id).Related(&currentSlave.Mongods, "Mongods").Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	if len(currentSlave.Mongods) != 0 {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprintf(w, "slave with id %d has active Mongods", currentSlave.ID)
		tx.Rollback()
		return
	}

	// Allow delete

	s := tx.Delete(&model.Slave{ID: id})
	if s.Error != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	if s.RowsAffected > 1 {
		log.Printf("inconsistency: slave DELETE affected more than one row. Slave.ID = %v", id)
	}

	if s.RowsAffected == 0 {
		w.WriteHeader(http.StatusNotFound)
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()
}

func changeToSlaveAllowed(db *gorm.DB, currentSlave *model.Slave, updatedSlave *model.Slave) (permissionError, dbError error) {

	//Allow change of state if nothing else is changed
	if currentSlave.ID == updatedSlave.ID &&
		currentSlave.Hostname == updatedSlave.Hostname &&
		currentSlave.Port == updatedSlave.Port &&
		currentSlave.MongodPortRangeBegin == updatedSlave.MongodPortRangeBegin &&
		currentSlave.MongodPortRangeEnd == updatedSlave.MongodPortRangeEnd &&
		currentSlave.PersistentStorage == updatedSlave.PersistentStorage &&
		currentSlave.RiskGroupID == updatedSlave.RiskGroupID {
		return nil, nil
	}
	if currentSlave.ConfiguredState != model.SlaveStateDisabled {
		return fmt.Errorf("slave's desired state must be = disabled"), nil
	}

	if err := db.Model(&currentSlave).Related(&currentSlave.Mongods, "Mongods").Error; err != nil {
		return nil, err
	}

	if len(currentSlave.Mongods) != 0 {
		return fmt.Errorf("slave has active Mongods"), nil
	}

	return nil, nil

}
<commit_msg>prettify: masterapi/slaves: changeToSlaveAllowed<commit_after>package masterapi

import (
	"encoding/json"
	"fmt"
	"github.com/KIT-MAMID/mamid/model"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"github.com/mattn/go-sqlite3"
	"log"
	"net/http"
	"strconv"
)

type Slave struct {
	ID                   uint   `json:"id"`
	Hostname             string `json:"hostname"`
	Port                 uint   `json:"slave_port"`
	MongodPortRangeBegin uint   `json:"mongod_port_range_begin"` //inclusive
	MongodPortRangeEnd   uint   `json:"mongod_port_range_end"`   //exclusive
	PersistentStorage    bool   `json:"persistent_storage"`
	ConfiguredState      string `json:"configured_state"`
	RiskGroupID          uint   `json:"risk_group_id"`
}

func (m *MasterAPI) SlaveIndex(w http.ResponseWriter, r *http.Request) {

	var slaves []*model.Slave
	err := m.DB.Order("id", false).Find(&slaves).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	out := make([]*Slave, len(slaves))
	for i, v := range slaves {
		out[i] = ProjectModelSlaveToSlave(v)
	}
	json.NewEncoder(w).Encode(out)
}

func (m *MasterAPI) SlaveById(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	if id == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "id may not be 0")
		return
	}

	var slave model.Slave
	res := m.DB.First(&slave, id)

	if res.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if err = res.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(&slave))
	return
}

func (m *MasterAPI) SlavePut(w http.ResponseWriter, r *http.Request) {
	var postSlave Slave
	err := json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not specify the slave ID in PUT request")
		return
	}

	modelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	}

	// Persist to database

	tx := m.DB.Begin()

	err = tx.Create(&modelSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok && driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, driverErr.Error())
		tx.Rollback()
		return
	}

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

	// Return created slave

	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(modelSlave))

	return
}

func (m *MasterAPI) SlaveUpdate(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	var postSlave Slave
	err = json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != id {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not change the id of an object")
		return
	}

	if err = postSlave.assertNoZeroFieldsSet(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not POST JSON with zero values in any field: %s", err.Error())
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	tx := m.DB.Begin()

	var modelSlave model.Slave
	modelSlaveRes := tx.First(&modelSlave, id)
	if modelSlaveRes.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	} else if err = modelSlaveRes.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	updatedModelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	permissionError, dbError := changeToSlaveAllowed(tx, &modelSlave, updatedModelSlave)
	if dbError != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, dbError)
		tx.Rollback()
		return
	}
	if permissionError != nil {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprint(w, permissionError)
		tx.Rollback()
		return
	}

	// Persist to database
	err = tx.Save(&updatedModelSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok {
		if driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprint(w, driverErr.Error())
			tx.Rollback()
			return
		}
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

}

func (m *MasterAPI) SlaveDelete(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	tx := m.DB.Begin()

	// Can only delete disabled slaves
	var currentSlave model.Slave
	if err = tx.First(&currentSlave, id).Related(&currentSlave.Mongods, "Mongods").Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	if len(currentSlave.Mongods) != 0 {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprintf(w, "slave with id %d has active Mongods", currentSlave.ID)
		tx.Rollback()
		return
	}

	// Allow delete

	s := tx.Delete(&model.Slave{ID: id})
	if s.Error != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	if s.RowsAffected > 1 {
		log.Printf("inconsistency: slave DELETE affected more than one row. Slave.ID = %v", id)
	}

	if s.RowsAffected == 0 {
		w.WriteHeader(http.StatusNotFound)
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()
}

func changeToSlaveAllowed(tx *gorm.DB, currentSlave *model.Slave, updatedSlave *model.Slave) (permissionError, dbError error) {

	//Allow change of state if nothing else is changed
	if currentSlave.ID == updatedSlave.ID &&
		currentSlave.Hostname == updatedSlave.Hostname &&
		currentSlave.Port == updatedSlave.Port &&
		currentSlave.MongodPortRangeBegin == updatedSlave.MongodPortRangeBegin &&
		currentSlave.MongodPortRangeEnd == updatedSlave.MongodPortRangeEnd &&
		currentSlave.PersistentStorage == updatedSlave.PersistentStorage &&
		currentSlave.RiskGroupID == updatedSlave.RiskGroupID {
		return nil, nil
	}
	if currentSlave.ConfiguredState != model.SlaveStateDisabled {
		return fmt.Errorf("slave's desired state must be `disabled`"), nil
	}

	if err := tx.Model(&currentSlave).Related(&currentSlave.Mongods, "Mongods").Error; err != nil {
		return nil, err
	}

	if len(currentSlave.Mongods) != 0 {
		return fmt.Errorf("slave has active Mongods"), nil
	}

	return nil, nil

}
<|endoftext|>


<commit_before>package docgen_test

import (
	. "github.com/antonmedv/expr/docgen"
	"github.com/sanity-io/litter"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

type Tweet struct {
	Size    int
	Message string
}

type Env struct {
	Tweets []Tweet
	Config struct {
		MaxSize int32
	}
	Env map[string]interface{}
}

func (*Env) Duration(s string) time.Duration {
	d, _ := time.ParseDuration(s)
	return d
}

func TestCreateDoc(t *testing.T) {
	doc := CreateDoc(&Env{})
	expected := &Context{
		Variables: map[Identifier]*Type{
			"Tweets": {
				Kind: "array",
				Type: &Type{
					Kind: "struct",
					Name: "Tweet",
				},
			},
			"Config": {
				Kind: "struct",
				Fields: map[Identifier]*Type{
					"MaxSize": {Kind: "int"},
				},
			},
			"Env": {
				Kind: "map",
				Key:  &Type{Kind: "string"},
				Type: &Type{Kind: "any"},
			},
			"Duration": {
				Kind: "func",
				Arguments: []*Type{
					{Kind: "string"},
				},
				Return: &Type{Kind: "struct", Name: "Duration"},
			},
		},
		Types: map[TypeName]*Type{
			"Tweet": {
				Kind: "struct",
				Fields: map[Identifier]*Type{
					"Size":    {Kind: "int"},
					"Message": {Kind: "string"},
				},
			},
			"Duration": {
				Kind: "struct",
				Fields: map[Identifier]*Type{
					"Hours": {
						Kind:      "func",
						Arguments: []*Type{},
						Return: &Type{
							Kind: "float",
						},
					},
					"Minutes": {
						Kind:      "func",
						Arguments: []*Type{},
						Return: &Type{
							Kind: "float",
						},
					},
					"Nanoseconds": {
						Kind:      "func",
						Arguments: []*Type{},
						Return: &Type{
							Kind: "int",
						},
					},
					"Round": {
						Kind: "func",
						Arguments: []*Type{
							{
								Name: "Duration",
								Kind: "struct",
							},
						},
						Return: &Type{
							Name: "Duration",
							Kind: "struct",
						},
					},
					"Seconds": {
						Kind:      "func",
						Arguments: []*Type{},
						Return: &Type{
							Kind: "float",
						},
					},
					"String": {
						Kind:      "func",
						Arguments: []*Type{},
						Return: &Type{
							Kind: "string",
						},
					},
					"Truncate": {
						Kind: "func",
						Arguments: []*Type{
							{
								Name: "Duration",
								Kind: "struct",
							},
						},
						Return: &Type{
							Name: "Duration",
							Kind: "struct",
						},
					},
				},
			},
		},
	}
	assert.Equal(t, litter.Sdump(expected), litter.Sdump(doc))
}
<commit_msg>Add docgen from map<commit_after>package docgen_test

import (
	. "github.com/antonmedv/expr/docgen"
	"github.com/sanity-io/litter"
	"github.com/stretchr/testify/assert"
	"math"
	"testing"
)

type Tweet struct {
	Size    int
	Message string
}

type Env struct {
	Tweets []Tweet
	Config struct {
		MaxSize int32
	}
	Env map[string]interface{}
}

type Duration int

func (Duration) String() string {
	return ""
}

func (*Env) Duration(s string) Duration {
	return Duration(0)
}

func TestCreateDoc(t *testing.T) {
	doc := CreateDoc(&Env{})
	expected := &Context{
		Variables: map[Identifier]*Type{
			"Tweets": {
				Kind: "array",
				Type: &Type{
					Kind: "struct",
					Name: "Tweet",
				},
			},
			"Config": {
				Kind: "struct",
				Fields: map[Identifier]*Type{
					"MaxSize": {Kind: "int"},
				},
			},
			"Env": {
				Kind: "map",
				Key:  &Type{Kind: "string"},
				Type: &Type{Kind: "any"},
			},
			"Duration": {
				Kind: "func",
				Arguments: []*Type{
					{Kind: "string"},
				},
				Return: &Type{Kind: "struct", Name: "Duration"},
			},
		},
		Types: map[TypeName]*Type{
			"Tweet": {
				Kind: "struct",
				Fields: map[Identifier]*Type{
					"Size":    {Kind: "int"},
					"Message": {Kind: "string"},
				},
			},
			"Duration": {
				Kind: "struct",
				Fields: map[Identifier]*Type{
					"String": {
						Kind:      "func",
						Arguments: []*Type{},
						Return: &Type{
							Kind: "string",
						},
					},
				},
			},
		},
	}

	assert.Equal(t, litter.Sdump(expected), litter.Sdump(doc))
}

func TestCreateDoc_FromMap(t *testing.T) {
	env := map[string]interface{}{
		"Tweets": []*Tweet{},
		"Config": struct {
			MaxSize int
		}{},
		"Max": math.Max,
	}
	doc := CreateDoc(env)
	expected := &Context{
		Variables: map[Identifier]*Type{
			"Tweets": {
				Kind: "array",
				Type: &Type{
					Kind: "struct",
					Name: "Tweet",
				},
			},
			"Config": {
				Kind: "struct",
				Fields: map[Identifier]*Type{
					"MaxSize": {Kind: "int"},
				},
			},
			"Max": {
				Kind: "func",
				Arguments: []*Type{
					{Kind: "float"},
					{Kind: "float"},
				},
				Return: &Type{Kind: "float"},
			},
		},
		Types: map[TypeName]*Type{
			"Tweet": {
				Kind: "struct",
				Fields: map[Identifier]*Type{
					"Size":    {Kind: "int"},
					"Message": {Kind: "string"},
				},
			},
		},
	}

	assert.Equal(t, litter.Sdump(expected), litter.Sdump(doc))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path"
	"sync"
	"syscall"
	"time"
)

import "git.torproject.org/pluggable-transports/goptlib.git"

const ptMethodName = "meek"
const minSessionIdLength = 32
const maxPayloadLength = 0x10000
const maxSessionStaleness = 120 * time.Second

var ptInfo pt.ServerInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func httpBadRequest(w http.ResponseWriter) {
	http.Error(w, "Bad request.\n", http.StatusBadRequest)
}

func httpInternalServerError(w http.ResponseWriter) {
	http.Error(w, "Internal server error.\n", http.StatusInternalServerError)
}

type Session struct {
	Or       *net.TCPConn
	LastSeen time.Time
}

func (session *Session) Touch() {
	session.LastSeen = time.Now()
}

func (session *Session) Expired() bool {
	return time.Since(session.LastSeen) > maxSessionStaleness
}

type State struct {
	sessionMap map[string]*Session
	lock       sync.Mutex
}

func NewState() *State {
	state := new(State)
	state.sessionMap = make(map[string]*Session)
	return state
}

func (state *State) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	switch req.Method {
	case "GET":
		state.Get(w, req)
	case "POST":
		state.Post(w, req)
	default:
		httpBadRequest(w)
	}
}

func (state *State) Get(w http.ResponseWriter, req *http.Request) {
	if path.Clean(req.URL.Path) != "/" {
		http.NotFound(w, req)
		return
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("I’m just a happy little web server.\n"))
}

func (state *State) getSession(sessionId string, req *http.Request) (*Session, error) {
	state.lock.Lock()
	defer state.lock.Unlock()

	session := state.sessionMap[sessionId]
	if session != nil {
		session.Touch()
		return session, nil
	}

	log.Printf("unknown session id %q; creating new session", sessionId)

	or, err := pt.DialOr(&ptInfo, req.RemoteAddr, ptMethodName)
	if err != nil {
		return nil, err
	}
	session = &Session{Or: or}
	state.sessionMap[sessionId] = session
	session.Touch()

	return session, nil
}

func (state *State) Post(w http.ResponseWriter, req *http.Request) {
	sessionId := req.Header.Get("X-Session-Id")
	if len(sessionId) < minSessionIdLength {
		httpBadRequest(w)
		return
	}

	session, err := state.getSession(sessionId, req)
	if err != nil {
		log.Print(err)
		httpInternalServerError(w)
		return
	}

	body := http.MaxBytesReader(w, req.Body, maxPayloadLength)
	_, err = io.Copy(session.Or, body)
	if err != nil {
		log.Printf("error copying body to ORPort: %s", err)
		return
	}

	buf := make([]byte, maxPayloadLength)
	session.Or.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
	n, err := session.Or.Read(buf)
	if err != nil {
		if e, ok := err.(net.Error); !ok || !e.Timeout() {
			log.Printf("error reading from ORPort: %s", err)
			return
		}
	}
	// log.Printf("read %d bytes from ORPort: %q", n, buf[:n])
	n, err = w.Write(buf[:n])
	if err != nil {
		log.Printf("error writing to response: %s", err)
		return
	}
	// log.Printf("wrote %d bytes to response", n)
}

func (state *State) ExpireSessions() {
	for {
		time.Sleep(maxSessionStaleness)
		state.lock.Lock()
		for sessionId, session := range state.sessionMap {
			if session.Expired() {
				log.Printf("deleting expired session %q", sessionId)
				delete(state.sessionMap, sessionId)
			}
		}
		state.lock.Unlock()
	}
}

func startListener(network string, addr *net.TCPAddr) (net.Listener, error) {
	ln, err := net.ListenTCP(network, addr)
	if err != nil {
		return nil, err
	}
	state := NewState()
	go state.ExpireSessions()
	server := &http.Server{
		Handler: state,
	}
	go func() {
		defer ln.Close()
		err = server.Serve(ln)
		if err != nil {
			log.Printf("Error in Serve: %s", err)
		}
	}()
	return ln, nil
}

func main() {
	var logFilename string
	var port int

	flag.StringVar(&logFilename, "log", "", "name of log file")
	flag.IntVar(&port, "port", 0, "port to listen on")
	flag.Parse()

	if logFilename != "" {
		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			log.Fatalf("error opening log file: %s", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	var err error
	ptInfo, err = pt.ServerSetup([]string{ptMethodName})
	if err != nil {
		log.Fatalf("error in ServerSetup: %s", err)
	}

	log.Printf("starting")
	listeners := make([]net.Listener, 0)
	for _, bindaddr := range ptInfo.Bindaddrs {
		if port != 0 {
			bindaddr.Addr.Port = port
		}
		switch bindaddr.MethodName {
		case ptMethodName:
			ln, err := startListener("tcp", bindaddr.Addr)
			if err != nil {
				pt.SmethodError(bindaddr.MethodName, err.Error())
				break
			}
			pt.Smethod(bindaddr.MethodName, ln.Addr())
			log.Printf("listening on %s", ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.SmethodError(bindaddr.MethodName, "no such method")
		}
	}
	pt.SmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}

	if sig == syscall.SIGTERM {
		return
	}

	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}

	log.Printf("done")
}
<commit_msg>Clearer.<commit_after>package main

import (
	"flag"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path"
	"sync"
	"syscall"
	"time"
)

import "git.torproject.org/pluggable-transports/goptlib.git"

const ptMethodName = "meek"
const minSessionIdLength = 32
const maxPayloadLength = 0x10000
const maxSessionStaleness = 120 * time.Second

var ptInfo pt.ServerInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func httpBadRequest(w http.ResponseWriter) {
	http.Error(w, "Bad request.\n", http.StatusBadRequest)
}

func httpInternalServerError(w http.ResponseWriter) {
	http.Error(w, "Internal server error.\n", http.StatusInternalServerError)
}

type Session struct {
	Or       *net.TCPConn
	LastSeen time.Time
}

func (session *Session) Touch() {
	session.LastSeen = time.Now()
}

func (session *Session) Expired() bool {
	return time.Since(session.LastSeen) > maxSessionStaleness
}

type State struct {
	sessionMap map[string]*Session
	lock       sync.Mutex
}

func NewState() *State {
	state := new(State)
	state.sessionMap = make(map[string]*Session)
	return state
}

func (state *State) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	switch req.Method {
	case "GET":
		state.Get(w, req)
	case "POST":
		state.Post(w, req)
	default:
		httpBadRequest(w)
	}
}

func (state *State) Get(w http.ResponseWriter, req *http.Request) {
	if path.Clean(req.URL.Path) != "/" {
		http.NotFound(w, req)
		return
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("I’m just a happy little web server.\n"))
}

func (state *State) getSession(sessionId string, req *http.Request) (*Session, error) {
	state.lock.Lock()
	defer state.lock.Unlock()

	session := state.sessionMap[sessionId]
	if session == nil {
		log.Printf("unknown session id %q; creating new session", sessionId)

		or, err := pt.DialOr(&ptInfo, req.RemoteAddr, ptMethodName)
		if err != nil {
			return nil, err
		}
		session = &Session{Or: or}
		state.sessionMap[sessionId] = session
	}
	session.Touch()

	return session, nil
}

func (state *State) Post(w http.ResponseWriter, req *http.Request) {
	sessionId := req.Header.Get("X-Session-Id")
	if len(sessionId) < minSessionIdLength {
		httpBadRequest(w)
		return
	}

	session, err := state.getSession(sessionId, req)
	if err != nil {
		log.Print(err)
		httpInternalServerError(w)
		return
	}

	body := http.MaxBytesReader(w, req.Body, maxPayloadLength)
	_, err = io.Copy(session.Or, body)
	if err != nil {
		log.Printf("error copying body to ORPort: %s", err)
		return
	}

	buf := make([]byte, maxPayloadLength)
	session.Or.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
	n, err := session.Or.Read(buf)
	if err != nil {
		if e, ok := err.(net.Error); !ok || !e.Timeout() {
			log.Printf("error reading from ORPort: %s", err)
			return
		}
	}
	// log.Printf("read %d bytes from ORPort: %q", n, buf[:n])
	n, err = w.Write(buf[:n])
	if err != nil {
		log.Printf("error writing to response: %s", err)
		return
	}
	// log.Printf("wrote %d bytes to response", n)
}

func (state *State) ExpireSessions() {
	for {
		time.Sleep(maxSessionStaleness)
		state.lock.Lock()
		for sessionId, session := range state.sessionMap {
			if session.Expired() {
				log.Printf("deleting expired session %q", sessionId)
				delete(state.sessionMap, sessionId)
			}
		}
		state.lock.Unlock()
	}
}

func startListener(network string, addr *net.TCPAddr) (net.Listener, error) {
	ln, err := net.ListenTCP(network, addr)
	if err != nil {
		return nil, err
	}
	state := NewState()
	go state.ExpireSessions()
	server := &http.Server{
		Handler: state,
	}
	go func() {
		defer ln.Close()
		err = server.Serve(ln)
		if err != nil {
			log.Printf("Error in Serve: %s", err)
		}
	}()
	return ln, nil
}

func main() {
	var logFilename string
	var port int

	flag.StringVar(&logFilename, "log", "", "name of log file")
	flag.IntVar(&port, "port", 0, "port to listen on")
	flag.Parse()

	if logFilename != "" {
		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			log.Fatalf("error opening log file: %s", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	var err error
	ptInfo, err = pt.ServerSetup([]string{ptMethodName})
	if err != nil {
		log.Fatalf("error in ServerSetup: %s", err)
	}

	log.Printf("starting")
	listeners := make([]net.Listener, 0)
	for _, bindaddr := range ptInfo.Bindaddrs {
		if port != 0 {
			bindaddr.Addr.Port = port
		}
		switch bindaddr.MethodName {
		case ptMethodName:
			ln, err := startListener("tcp", bindaddr.Addr)
			if err != nil {
				pt.SmethodError(bindaddr.MethodName, err.Error())
				break
			}
			pt.Smethod(bindaddr.MethodName, ln.Addr())
			log.Printf("listening on %s", ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.SmethodError(bindaddr.MethodName, "no such method")
		}
	}
	pt.SmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}

	if sig == syscall.SIGTERM {
		return
	}

	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}

	log.Printf("done")
}
<|endoftext|>


<commit_before>package controller

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/tidwall/resp"
	"github.com/tidwall/tile38/controller/glob"
	"github.com/tidwall/tile38/controller/server"
)

const (
	defaultKeepAlive = 300 // seconds
)

const (
	RequirePass   = "requirepass"
	LeaderAuth    = "leaderauth"
	ProtectedMode = "protected-mode"
	MaxMemory     = "maxmemory"
	AutoGC        = "autogc"
	KeepAlive     = "keepalive"
)

var validProperties = []string{RequirePass, LeaderAuth, ProtectedMode, MaxMemory, AutoGC, KeepAlive}

// Config is a tile38 config
type Config struct {
	FollowHost string `json:"follow_host,omitempty"`
	FollowPort int    `json:"follow_port,omitempty"`
	FollowID   string `json:"follow_id,omitempty"`
	FollowPos  int    `json:"follow_pos,omitempty"`
	ServerID   string `json:"server_id,omitempty"`
	ReadOnly   bool   `json:"read_only,omitempty"`

	// Properties
	RequirePassP   string `json:"requirepass,omitempty"`
	RequirePass    string `json:"-"`
	LeaderAuthP    string `json:"leaderauth,omitempty"`
	LeaderAuth     string `json:"-"`
	ProtectedModeP string `json:"protected-mode,omitempty"`
	ProtectedMode  string `json:"-"`
	MaxMemoryP     string `json:"maxmemory,omitempty"`
	MaxMemory      int    `json:"-"`
	AutoGCP        string `json:"autogc,omitempty"`
	AutoGC         uint64 `json:"-"`
	KeepAliveP     string `json:"keepalive,omitempty"`
	KeepAlive      int    `json:"-"`
}

func (c *Controller) loadConfig() error {
	data, err := ioutil.ReadFile(c.dir + "/config")
	if err != nil {
		if os.IsNotExist(err) {
			return c.initConfig()
		}
		return err
	}
	err = json.Unmarshal(data, &c.config)
	if err != nil {
		return err
	}
	// load properties
	if err := c.setConfigProperty(RequirePass, c.config.RequirePassP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(LeaderAuth, c.config.LeaderAuthP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(ProtectedMode, c.config.ProtectedModeP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(MaxMemory, c.config.MaxMemoryP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(AutoGC, c.config.AutoGCP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(KeepAlive, c.config.KeepAliveP, true); err != nil {
		return err
	}
	return nil
}

func parseMemSize(s string) (bytes int, ok bool) {
	if s == "" {
		return 0, true
	}
	s = strings.ToLower(s)
	var n uint64
	var sz int
	var err error
	if strings.HasSuffix(s, "gb") {
		n, err = strconv.ParseUint(s[:len(s)-2], 10, 64)
		sz = int(n * 1024 * 1024 * 1024)
	} else if strings.HasSuffix(s, "mb") {
		n, err = strconv.ParseUint(s[:len(s)-2], 10, 64)
		sz = int(n * 1024 * 1024)
	} else if strings.HasSuffix(s, "kb") {
		n, err = strconv.ParseUint(s[:len(s)-2], 10, 64)
		sz = int(n * 1024)
	} else {
		n, err = strconv.ParseUint(s, 10, 64)
		sz = int(n)
	}
	if err != nil {
		return 0, false
	}
	return sz, true
}

func formatMemSize(sz int) string {
	if sz <= 0 {
		return ""
	}
	if sz < 1024 {
		return strconv.FormatInt(int64(sz), 10)
	}
	sz /= 1024
	if sz < 1024 {
		return strconv.FormatInt(int64(sz), 10) + "kb"
	}
	sz /= 1024
	if sz < 1024 {
		return strconv.FormatInt(int64(sz), 10) + "mb"
	}
	sz /= 1024
	return strconv.FormatInt(int64(sz), 10) + "gb"
}

func (c *Controller) setConfigProperty(name, value string, fromLoad bool) error {
	var invalid bool
	switch name {
	default:
		return fmt.Errorf("Unsupported CONFIG parameter: %s", name)
	case RequirePass:
		c.config.RequirePass = value
	case LeaderAuth:
		c.config.LeaderAuth = value
	case AutoGC:
		if value == "" {
			c.config.AutoGC = 0
		} else {
			gc, err := strconv.ParseUint(value, 10, 64)
			if err != nil {
				return err
			}
			c.config.AutoGC = gc
		}
	case MaxMemory:
		sz, ok := parseMemSize(value)
		if !ok {
			return fmt.Errorf("Invalid argument '%s' for CONFIG SET '%s'", value, name)
		}
		c.config.MaxMemory = sz
	case ProtectedMode:
		switch strings.ToLower(value) {
		case "":
			if fromLoad {
				c.config.ProtectedMode = "yes"
			} else {
				invalid = true
			}
		case "yes", "no":
			c.config.ProtectedMode = strings.ToLower(value)
		default:
			invalid = true
		}
	case KeepAlive:
		if value == "" {
			c.config.KeepAlive = defaultKeepAlive
		} else {
			keepalive, err := strconv.ParseUint(value, 10, 64)
			if err != nil {
				invalid = true
			} else {
				c.config.KeepAlive = int(keepalive)
			}
		}
	}

	if invalid {
		return fmt.Errorf("Invalid argument '%s' for CONFIG SET '%s'", value, name)
	}
	return nil
}

func (c *Controller) getConfigProperties(pattern string) map[string]interface{} {
	m := make(map[string]interface{})
	for _, name := range validProperties {
		matched, _ := glob.Match(pattern, name)
		if matched {
			m[name] = c.getConfigProperty(name)
		}
	}
	return m
}
func (c *Controller) getConfigProperty(name string) string {
	switch name {
	default:
		return ""
	case AutoGC:
		return strconv.FormatUint(c.config.AutoGC, 10)
	case RequirePass:
		return c.config.RequirePass
	case LeaderAuth:
		return c.config.LeaderAuth
	case ProtectedMode:
		return c.config.ProtectedMode
	case MaxMemory:
		return formatMemSize(c.config.MaxMemory)
	case KeepAlive:
		return strconv.FormatUint(uint64(c.config.KeepAlive), 10)
	}
}

func (c *Controller) initConfig() error {
	c.config = Config{ServerID: randomKey(16)}
	c.config.KeepAlive = defaultKeepAlive
	return c.writeConfig(true)
}

func (c *Controller) writeConfig(writeProperties bool) error {
	var err error
	bak := c.config
	defer func() {
		if err != nil {
			// revert changes
			c.config = bak
		}
	}()
	if writeProperties {
		// save properties
		c.config.RequirePassP = c.config.RequirePass
		c.config.LeaderAuthP = c.config.LeaderAuth
		c.config.ProtectedModeP = c.config.ProtectedMode
		c.config.MaxMemoryP = formatMemSize(c.config.MaxMemory)
		c.config.AutoGCP = strconv.FormatUint(c.config.AutoGC, 10)
		c.config.KeepAliveP = strconv.FormatUint(uint64(c.config.KeepAlive), 10)
	}
	var data []byte
	data, err = json.MarshalIndent(c.config, "", "\t")
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(c.dir+"/config", data, 0600)
	if err != nil {
		return err
	}
	return nil
}

func (c *Controller) cmdConfigGet(msg *server.Message) (res string, err error) {
	start := time.Now()
	vs := msg.Values[1:]
	var ok bool
	var name string
	if vs, name, ok = tokenval(vs); !ok {
		return "", errInvalidNumberOfArguments
	}
	if len(vs) != 0 {
		return "", errInvalidNumberOfArguments
	}
	m := c.getConfigProperties(name)
	switch msg.OutputType {
	case server.JSON:
		data, err := json.Marshal(m)
		if err != nil {
			return "", err
		}
		res = `{"ok":true,"properties":` + string(data) + `,"elapsed":"` + time.Now().Sub(start).String() + "\"}"
	case server.RESP:
		vals := respValuesSimpleMap(m)
		data, err := resp.ArrayValue(vals).MarshalRESP()
		if err != nil {
			return "", err
		}
		res = string(data)
	}
	return
}
func (c *Controller) cmdConfigSet(msg *server.Message) (res string, err error) {
	start := time.Now()
	vs := msg.Values[1:]
	var ok bool
	var name string
	if vs, name, ok = tokenval(vs); !ok {
		return "", errInvalidNumberOfArguments
	}
	var value string
	if vs, value, ok = tokenval(vs); !ok {
		if strings.ToLower(name) != RequirePass {
			return "", errInvalidNumberOfArguments
		}
	}
	if len(vs) != 0 {
		return "", errInvalidNumberOfArguments
	}
	if err := c.setConfigProperty(name, value, false); err != nil {
		return "", err
	}
	return server.OKMessage(msg, start), nil
}
func (c *Controller) cmdConfigRewrite(msg *server.Message) (res string, err error) {
	start := time.Now()
	vs := msg.Values[1:]
	if len(vs) != 0 {
		return "", errInvalidNumberOfArguments
	}
	if err := c.writeConfig(true); err != nil {
		return "", err
	}
	return server.OKMessage(msg, start), nil
}
<commit_msg>omit defaults on rewrite<commit_after>package controller

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/tidwall/resp"
	"github.com/tidwall/tile38/controller/glob"
	"github.com/tidwall/tile38/controller/server"
)

const (
	defaultKeepAlive     = 300 // seconds
	defaultProtectedMode = "yes"
)

const (
	RequirePass   = "requirepass"
	LeaderAuth    = "leaderauth"
	ProtectedMode = "protected-mode"
	MaxMemory     = "maxmemory"
	AutoGC        = "autogc"
	KeepAlive     = "keepalive"
)

var validProperties = []string{RequirePass, LeaderAuth, ProtectedMode, MaxMemory, AutoGC, KeepAlive}

// Config is a tile38 config
type Config struct {
	FollowHost string `json:"follow_host,omitempty"`
	FollowPort int    `json:"follow_port,omitempty"`
	FollowID   string `json:"follow_id,omitempty"`
	FollowPos  int    `json:"follow_pos,omitempty"`
	ServerID   string `json:"server_id,omitempty"`
	ReadOnly   bool   `json:"read_only,omitempty"`

	// Properties
	RequirePassP   string `json:"requirepass,omitempty"`
	RequirePass    string `json:"-"`
	LeaderAuthP    string `json:"leaderauth,omitempty"`
	LeaderAuth     string `json:"-"`
	ProtectedModeP string `json:"protected-mode,omitempty"`
	ProtectedMode  string `json:"-"`
	MaxMemoryP     string `json:"maxmemory,omitempty"`
	MaxMemory      int    `json:"-"`
	AutoGCP        string `json:"autogc,omitempty"`
	AutoGC         uint64 `json:"-"`
	KeepAliveP     string `json:"keepalive,omitempty"`
	KeepAlive      int    `json:"-"`
}

func (c *Controller) loadConfig() error {
	data, err := ioutil.ReadFile(c.dir + "/config")
	if err != nil {
		if os.IsNotExist(err) {
			return c.initConfig()
		}
		return err
	}
	err = json.Unmarshal(data, &c.config)
	if err != nil {
		return err
	}
	// load properties
	if err := c.setConfigProperty(RequirePass, c.config.RequirePassP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(LeaderAuth, c.config.LeaderAuthP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(ProtectedMode, c.config.ProtectedModeP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(MaxMemory, c.config.MaxMemoryP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(AutoGC, c.config.AutoGCP, true); err != nil {
		return err
	}
	if err := c.setConfigProperty(KeepAlive, c.config.KeepAliveP, true); err != nil {
		return err
	}
	return nil
}

func parseMemSize(s string) (bytes int, ok bool) {
	if s == "" {
		return 0, true
	}
	s = strings.ToLower(s)
	var n uint64
	var sz int
	var err error
	if strings.HasSuffix(s, "gb") {
		n, err = strconv.ParseUint(s[:len(s)-2], 10, 64)
		sz = int(n * 1024 * 1024 * 1024)
	} else if strings.HasSuffix(s, "mb") {
		n, err = strconv.ParseUint(s[:len(s)-2], 10, 64)
		sz = int(n * 1024 * 1024)
	} else if strings.HasSuffix(s, "kb") {
		n, err = strconv.ParseUint(s[:len(s)-2], 10, 64)
		sz = int(n * 1024)
	} else {
		n, err = strconv.ParseUint(s, 10, 64)
		sz = int(n)
	}
	if err != nil {
		return 0, false
	}
	return sz, true
}

func formatMemSize(sz int) string {
	if sz <= 0 {
		return ""
	}
	if sz < 1024 {
		return strconv.FormatInt(int64(sz), 10)
	}
	sz /= 1024
	if sz < 1024 {
		return strconv.FormatInt(int64(sz), 10) + "kb"
	}
	sz /= 1024
	if sz < 1024 {
		return strconv.FormatInt(int64(sz), 10) + "mb"
	}
	sz /= 1024
	return strconv.FormatInt(int64(sz), 10) + "gb"
}

func (c *Controller) setConfigProperty(name, value string, fromLoad bool) error {
	var invalid bool
	switch name {
	default:
		return fmt.Errorf("Unsupported CONFIG parameter: %s", name)
	case RequirePass:
		c.config.RequirePass = value
	case LeaderAuth:
		c.config.LeaderAuth = value
	case AutoGC:
		if value == "" {
			c.config.AutoGC = 0
		} else {
			gc, err := strconv.ParseUint(value, 10, 64)
			if err != nil {
				return err
			}
			c.config.AutoGC = gc
		}
	case MaxMemory:
		sz, ok := parseMemSize(value)
		if !ok {
			return fmt.Errorf("Invalid argument '%s' for CONFIG SET '%s'", value, name)
		}
		c.config.MaxMemory = sz
	case ProtectedMode:
		switch strings.ToLower(value) {
		case "":
			if fromLoad {
				c.config.ProtectedMode = defaultProtectedMode
			} else {
				invalid = true
			}
		case "yes", "no":
			c.config.ProtectedMode = strings.ToLower(value)
		default:
			invalid = true
		}
	case KeepAlive:
		if value == "" {
			c.config.KeepAlive = defaultKeepAlive
		} else {
			keepalive, err := strconv.ParseUint(value, 10, 64)
			if err != nil {
				invalid = true
			} else {
				c.config.KeepAlive = int(keepalive)
			}
		}
	}

	if invalid {
		return fmt.Errorf("Invalid argument '%s' for CONFIG SET '%s'", value, name)
	}
	return nil
}

func (c *Controller) getConfigProperties(pattern string) map[string]interface{} {
	m := make(map[string]interface{})
	for _, name := range validProperties {
		matched, _ := glob.Match(pattern, name)
		if matched {
			m[name] = c.getConfigProperty(name)
		}
	}
	return m
}
func (c *Controller) getConfigProperty(name string) string {
	switch name {
	default:
		return ""
	case AutoGC:
		return strconv.FormatUint(c.config.AutoGC, 10)
	case RequirePass:
		return c.config.RequirePass
	case LeaderAuth:
		return c.config.LeaderAuth
	case ProtectedMode:
		return c.config.ProtectedMode
	case MaxMemory:
		return formatMemSize(c.config.MaxMemory)
	case KeepAlive:
		return strconv.FormatUint(uint64(c.config.KeepAlive), 10)
	}
}

func (c *Controller) initConfig() error {
	c.config = Config{ServerID: randomKey(16)}
	return c.writeConfig(true)
}

func (c *Controller) writeConfig(writeProperties bool) error {
	var err error
	bak := c.config
	defer func() {
		if err != nil {
			// revert changes
			c.config = bak
		}
	}()
	if writeProperties {
		// save properties
		c.config.RequirePassP = c.config.RequirePass
		c.config.LeaderAuthP = c.config.LeaderAuth
		if c.config.ProtectedMode == defaultProtectedMode {
			c.config.ProtectedModeP = ""
		} else {
			c.config.ProtectedModeP = c.config.ProtectedMode
		}
		c.config.MaxMemoryP = formatMemSize(c.config.MaxMemory)
		if c.config.AutoGC == 0 {
			c.config.AutoGCP = ""
		} else {
			c.config.AutoGCP = strconv.FormatUint(c.config.AutoGC, 10)
		}
		if c.config.KeepAlive == defaultKeepAlive {
			c.config.KeepAliveP = ""
		} else {
			c.config.KeepAliveP = strconv.FormatUint(uint64(c.config.KeepAlive), 10)
		}
	}
	var data []byte
	data, err = json.MarshalIndent(c.config, "", "\t")
	if err != nil {
		return err
	}
	data = append(data, '\n')
	err = ioutil.WriteFile(c.dir+"/config", data, 0600)
	if err != nil {
		return err
	}
	return nil
}

func (c *Controller) cmdConfigGet(msg *server.Message) (res string, err error) {
	start := time.Now()
	vs := msg.Values[1:]
	var ok bool
	var name string
	if vs, name, ok = tokenval(vs); !ok {
		return "", errInvalidNumberOfArguments
	}
	if len(vs) != 0 {
		return "", errInvalidNumberOfArguments
	}
	m := c.getConfigProperties(name)
	switch msg.OutputType {
	case server.JSON:
		data, err := json.Marshal(m)
		if err != nil {
			return "", err
		}
		res = `{"ok":true,"properties":` + string(data) + `,"elapsed":"` + time.Now().Sub(start).String() + "\"}"
	case server.RESP:
		vals := respValuesSimpleMap(m)
		data, err := resp.ArrayValue(vals).MarshalRESP()
		if err != nil {
			return "", err
		}
		res = string(data)
	}
	return
}
func (c *Controller) cmdConfigSet(msg *server.Message) (res string, err error) {
	start := time.Now()
	vs := msg.Values[1:]
	var ok bool
	var name string
	if vs, name, ok = tokenval(vs); !ok {
		return "", errInvalidNumberOfArguments
	}
	var value string
	if vs, value, ok = tokenval(vs); !ok {
		if strings.ToLower(name) != RequirePass {
			return "", errInvalidNumberOfArguments
		}
	}
	if len(vs) != 0 {
		return "", errInvalidNumberOfArguments
	}
	if err := c.setConfigProperty(name, value, false); err != nil {
		return "", err
	}
	return server.OKMessage(msg, start), nil
}
func (c *Controller) cmdConfigRewrite(msg *server.Message) (res string, err error) {
	start := time.Now()
	vs := msg.Values[1:]
	if len(vs) != 0 {
		return "", errInvalidNumberOfArguments
	}
	if err := c.writeConfig(true); err != nil {
		return "", err
	}
	return server.OKMessage(msg, start), nil
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/Luncher/go-rest/forms"
	"github.com/Luncher/go-rest/models"
	"github.com/gin-gonic/gin"
	"strconv"
)

var movieModel = new(models.MovieModel)

type UserController struct{}

func (user *UserController) Create(c *gin.Context) {
	var data forms.CreateMovieCommand
	if c.BindJSON(&data) != nil {
		c.JSON(406, gin.H{"message": "Invalid form", "form": data})
		c.Abort()
		return
	}

	movieId, err := movieModel.Ceate(data)
	if err != nil {
		c.JSON(406, gin.H{"message": "Movie could not be created", "error": err.Error()})
		c.Abort()
		return
	}

	c.JSON(200, gin.H{"message": "Movie created", "id": movieId})
}

func (user *UserController) Get(c *gin.Context) {
	id := c.Param("id")
	if id, err := strconv.ParseInt(id, 10, 64); err != nil {
		c.JSON(404, gin.H{"message": "Invalid parameter"})
	} else {
		profile, err := movieModel.findOne(id)
		if err != nil {
			c.JSON(404, gin.H{"message": "Movie not found", "error": err.Error()})
			c.Abort()
		} else {
			c.JSON(200, gin.H{"data": profile})
		}
	}
}

func (user *UserController) Update(c *gin.Context) {
	id := c.Param("id")
	data := forms.UpdateMovieCommand{}

	if c.BindJSON(&data) != nil {
		c.JSON(406, gin.H{"message": "Invalid Parameters"})
		c.Abort()
		return
	}

	err := movieModel.Update(id, data)
	if err != nil {
		c.JSON(406, gin.H{"message": "movie count not be updated", "error": err.Error()})
		c.Abort()
		return
	}
	c.JSON(200, gin.H{"message": "Movie updated"})
}

func (user *UserController) Delete(c *gin.Context) {
	id := c.Param("id")
	err := movieModel.Delete(id)
	if err != nil {
		c.JSON(406, gin.H{"message": "Movie could not be deleted", "error": err.Error()})
		c.Abort()
		return
	}
	c.JSON(200, gin.H{"message": "Movie deleted"})
}
<commit_msg>fix models BUG<commit_after>package controllers

import (
	"github.com/Luncher/go-rest/forms"
	"github.com/Luncher/go-rest/models"
	"github.com/gin-gonic/gin"
	"strconv"
)

var movieModel = new(models.MovieModel)

type UserController struct{}

func (user *UserController) Create(c *gin.Context) {
	var data forms.CreateMovieCommand
	if c.BindJSON(&data) != nil {
		c.JSON(406, gin.H{"message": "Invalid form", "form": data})
		c.Abort()
		return
	}

	movieId, err := movieModel.Create(data)
	if err != nil {
		c.JSON(406, gin.H{"message": "Movie could not be created", "error": err.Error()})
		c.Abort()
		return
	}

	c.JSON(200, gin.H{"message": "Movie created", "id": movieId})
}

func (user *UserController) Get(c *gin.Context) {
	id := c.Param("id")
	if id, err := strconv.ParseInt(id, 10, 64); err != nil {
		c.JSON(404, gin.H{"message": "Invalid parameter"})
	} else {
		profile, err := movieModel.Get(id)
		if err != nil {
			c.JSON(404, gin.H{"message": "Movie not found", "error": err.Error()})
			c.Abort()
		} else {
			c.JSON(200, gin.H{"data": profile})
		}
	}
}

func (user *UserController) Update(c *gin.Context) {
	id := c.Param("id")
	data := forms.UpdateMovieCommand{}

	if c.BindJSON(&data) != nil {
		c.JSON(406, gin.H{"message": "Invalid Parameters"})
		c.Abort()
		return
	}

	err := movieModel.Update(id, data)
	if err != nil {
		c.JSON(406, gin.H{"message": "movie count not be updated", "error": err.Error()})
		c.Abort()
		return
	}
	c.JSON(200, gin.H{"message": "Movie updated"})
}

func (user *UserController) Delete(c *gin.Context) {
	id := c.Param("id")
	err := movieModel.Delete(id)
	if err != nil {
		c.JSON(406, gin.H{"message": "Movie could not be deleted", "error": err.Error()})
		c.Abort()
		return
	}
	c.JSON(200, gin.H{"message": "Movie deleted"})
}
<|endoftext|>


<commit_before>package gitlab

import (
	"fmt"
	"time"
)

// MergeRequestApprovalsService handles communication with the merge request
// approvals related methods of the GitLab API. This includes reading/updating
// approval settings and approve/unapproving merge requests
//
// GitLab API docs: https://docs.gitlab.com/ee/api/merge_request_approvals.html
type MergeRequestApprovalsService struct {
	client *Client
}

// MergeRequestApprovals represents GitLab merge request approvals.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals
type MergeRequestApprovals struct {
	ID                   int                          `json:"id"`
	ProjectID            int                          `json:"project_id"`
	Title                string                       `json:"title"`
	Description          string                       `json:"description"`
	State                string                       `json:"state"`
	CreatedAt            *time.Time                   `json:"created_at"`
	UpdatedAt            *time.Time                   `json:"updated_at"`
	MergeStatus          string                       `json:"merge_status"`
	ApprovalsBeforeMerge int                          `json:"approvals_before_merge"`
	ApprovalsRequired    int                          `json:"approvals_required"`
	ApprovalsLeft        int                          `json:"approvals_left"`
	ApprovedBy           []*MergeRequestApproverUser  `json:"approved_by"`
	Approvers            []*MergeRequestApproverUser  `json:"approvers"`
	ApproverGroups       []*MergeRequestApproverGroup `json:"approver_groups"`
	SuggestedApprovers   []*BasicUser                 `json:"suggested_approvers"`
}

func (m MergeRequestApprovals) String() string {
	return Stringify(m)
}

// MergeRequestApproverGroup  represents GitLab project level merge request approver group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals
type MergeRequestApproverGroup struct {
	Group struct {
		ID                   int    `json:"id"`
		Name                 string `json:"name"`
		Path                 string `json:"path"`
		Description          string `json:"description"`
		Visibility           string `json:"visibility"`
		AvatarURL            string `json:"avatar_url"`
		WebURL               string `json:"web_url"`
		FullName             string `json:"full_name"`
		FullPath             string `json:"full_path"`
		LFSEnabled           bool   `json:"lfs_enabled"`
		RequestAccessEnabled bool   `json:"request_access_enabled"`
	}
}

// MergeRequestApprovalRule represents a GitLab merge request approval rule.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules
type MergeRequestApprovalRule struct {
	ID                   int                                    `json:"id,omitempty"`
	Name                 string                                 `json:"name,omitempty"`
	RuleType             string                                 `json:"rule_type,omitempty"`
	EligibleApprovers    []*BasicUser                           `json:"eligible_approvers,omitempty"`
	ApprovalsRequired    int                                    `json:"approvals_required,omitempty"`
	SourceRule           *CreateMergeRequestApprovalRuleOptions `json:"source_rule,omitempty"`
	Users                []*BasicUser                           `json:"users,omitempty"`
	Groups               []*Group                               `json:"groups,omitempty"`
	ContainsHiddenGroups bool                                   `json:"contains_hidden_groups,omitempty"`
}

func (s MergeRequestApprovalRule) String() string {
	return Stringify(s)
}

// MergeRequestApproverUser  represents GitLab project level merge request approver user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals
type MergeRequestApproverUser struct {
	User *BasicUser
}

// ApproveMergeRequestOptions represents the available ApproveMergeRequest() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request
type ApproveMergeRequestOptions struct {
	SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
}

// ApproveMergeRequest approves a merge request on GitLab. If a non-empty sha
// is provided then it must match the sha at the HEAD of the MR.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request
func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr int, opt *ApproveMergeRequestOptions, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approve", pathEscape(project), mr)

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(MergeRequestApprovals)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// UnapproveMergeRequest unapproves a previously approved merge request on GitLab.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#unapprove-merge-request
func (s *MergeRequestApprovalsService) UnapproveMergeRequest(pid interface{}, mr int, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/unapprove", pathEscape(project), mr)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ChangeMergeRequestApprovalConfigurationOptions represents the available
// ChangeMergeRequestApprovalConfiguration() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration
type ChangeMergeRequestApprovalConfigurationOptions struct {
	ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
}

// ChangeApprovalConfiguration updates the approval configuration of a merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration
func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestApprovalConfigurationOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mergeRequestIID)

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(MergeRequest)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// ChangeMergeRequestAllowedApproversOptions represents the available
// ChangeMergeRequestAllowedApprovers() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request
type ChangeMergeRequestAllowedApproversOptions struct {
	ApproverIDs      []int `url:"approver_ids" json:"approver_ids"`
	ApproverGroupIDs []int `url:"approver_group_ids" json:"approver_group_ids"`
}

// ChangeAllowedApprovers updates the approvers for a merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request
func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestAllowedApproversOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approvers", pathEscape(project), mergeRequestIID)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(MergeRequest)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// GetApprovalRules requests information about a merge request’s approval rules
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules
func (s *MergeRequestApprovalsService) GetApprovalRules(pid interface{}, mergeRequestIID int, options ...OptionFunc) ([]*MergeRequestApprovalRule, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", pathEscape(project), mergeRequestIID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var par []*MergeRequestApprovalRule
	resp, err := s.client.Do(req, &par)
	if err != nil {
		return nil, resp, err
	}

	return par, resp, err
}

// CreateMergeRequestApprovalRuleOptions represents the available CreateApprovalRule()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule
type CreateMergeRequestApprovalRuleOptions struct {
	Name                  *string `url:"name,omitempty" json:"name,omitempty"`
	ApprovalsRequired     *int    `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
	ApprovalProjectRuleID *int    `url:"approval_project_rule_id,omitempty" json:"approval_project_rule_id,omitempty"`
	UserIDs               []int   `url:"user_ids,omitempty" json:"user_ids,omitempty"`
	GroupIDs              []int   `url:"group_ids,omitempty" json:"group_ids,omitempty"`
}

// CreateApprovalRule creates a new MR level approval rule.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule
func (s *MergeRequestApprovalsService) CreateApprovalRule(pid interface{}, mergeRequestIID int, opt *CreateMergeRequestApprovalRuleOptions, options ...OptionFunc) (*MergeRequestApprovalRule, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", pathEscape(project), mergeRequestIID)

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	par := new(MergeRequestApprovalRule)
	resp, err := s.client.Do(req, &par)
	if err != nil {
		return nil, resp, err
	}

	return par, resp, err
}

// UpdateMergeRequestApprovalRuleOptions represents the available UpdateApprovalRule()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule
type UpdateMergeRequestApprovalRuleOptions struct {
	Name              *string `url:"name,omitempty" json:"name,omitempty"`
	ApprovalsRequired *int    `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
	UserIDs           []int   `url:"user_ids,omitempty" json:"user_ids,omitempty"`
	GroupIDs          []int   `url:"group_ids,omitempty" json:"group_ids,omitempty"`
}

// UpdateApprovalRule updates an existing approval rule with new options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule
func (s *MergeRequestApprovalsService) UpdateApprovalRule(pid interface{}, mergeRequestIID int, approvalRule int, opt *UpdateMergeRequestApprovalRuleOptions, options ...OptionFunc) (*MergeRequestApprovalRule, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", pathEscape(project), mergeRequestIID, approvalRule)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	par := new(MergeRequestApprovalRule)
	resp, err := s.client.Do(req, &par)
	if err != nil {
		return nil, resp, err
	}

	return par, resp, err
}

// DeleteApprovalRule deletes a mr level approval rule.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#delete-merge-request-level-rule
func (s *MergeRequestApprovalsService) DeleteApprovalRule(pid interface{}, mergeRequestIID int, approvalRule int, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", pathEscape(project), mergeRequestIID, approvalRule)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<commit_msg>Remove omitempty from result struct, use proper source rule<commit_after>package gitlab

import (
	"fmt"
	"time"
)

// MergeRequestApprovalsService handles communication with the merge request
// approvals related methods of the GitLab API. This includes reading/updating
// approval settings and approve/unapproving merge requests
//
// GitLab API docs: https://docs.gitlab.com/ee/api/merge_request_approvals.html
type MergeRequestApprovalsService struct {
	client *Client
}

// MergeRequestApprovals represents GitLab merge request approvals.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals
type MergeRequestApprovals struct {
	ID                   int                          `json:"id"`
	ProjectID            int                          `json:"project_id"`
	Title                string                       `json:"title"`
	Description          string                       `json:"description"`
	State                string                       `json:"state"`
	CreatedAt            *time.Time                   `json:"created_at"`
	UpdatedAt            *time.Time                   `json:"updated_at"`
	MergeStatus          string                       `json:"merge_status"`
	ApprovalsBeforeMerge int                          `json:"approvals_before_merge"`
	ApprovalsRequired    int                          `json:"approvals_required"`
	ApprovalsLeft        int                          `json:"approvals_left"`
	ApprovedBy           []*MergeRequestApproverUser  `json:"approved_by"`
	Approvers            []*MergeRequestApproverUser  `json:"approvers"`
	ApproverGroups       []*MergeRequestApproverGroup `json:"approver_groups"`
	SuggestedApprovers   []*BasicUser                 `json:"suggested_approvers"`
}

func (m MergeRequestApprovals) String() string {
	return Stringify(m)
}

// MergeRequestApproverGroup  represents GitLab project level merge request approver group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals
type MergeRequestApproverGroup struct {
	Group struct {
		ID                   int    `json:"id"`
		Name                 string `json:"name"`
		Path                 string `json:"path"`
		Description          string `json:"description"`
		Visibility           string `json:"visibility"`
		AvatarURL            string `json:"avatar_url"`
		WebURL               string `json:"web_url"`
		FullName             string `json:"full_name"`
		FullPath             string `json:"full_path"`
		LFSEnabled           bool   `json:"lfs_enabled"`
		RequestAccessEnabled bool   `json:"request_access_enabled"`
	}
}

// MergeRequestApprovalRule represents a GitLab merge request approval rule.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules
type MergeRequestApprovalRule struct {
	ID                   int                  `json:"id"`
	Name                 string               `json:"name"`
	RuleType             string               `json:"rule_type"`
	EligibleApprovers    []*BasicUser         `json:"eligible_approvers"`
	ApprovalsRequired    int                  `json:"approvals_required"`
	SourceRule           *ProjectApprovalRule `json:"source_rule"`
	Users                []*BasicUser         `json:"users"`
	Groups               []*Group             `json:"groups"`
	ContainsHiddenGroups bool                 `json:"contains_hidden_groups"`
}

// String is a stringify for MergeRequestApprovalRule
func (s MergeRequestApprovalRule) String() string {
	return Stringify(s)
}

// MergeRequestApproverUser  represents GitLab project level merge request approver user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals
type MergeRequestApproverUser struct {
	User *BasicUser
}

// ApproveMergeRequestOptions represents the available ApproveMergeRequest() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request
type ApproveMergeRequestOptions struct {
	SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
}

// ApproveMergeRequest approves a merge request on GitLab. If a non-empty sha
// is provided then it must match the sha at the HEAD of the MR.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request
func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr int, opt *ApproveMergeRequestOptions, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approve", pathEscape(project), mr)

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(MergeRequestApprovals)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// UnapproveMergeRequest unapproves a previously approved merge request on GitLab.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#unapprove-merge-request
func (s *MergeRequestApprovalsService) UnapproveMergeRequest(pid interface{}, mr int, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/unapprove", pathEscape(project), mr)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ChangeMergeRequestApprovalConfigurationOptions represents the available
// ChangeMergeRequestApprovalConfiguration() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration
type ChangeMergeRequestApprovalConfigurationOptions struct {
	ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
}

// ChangeApprovalConfiguration updates the approval configuration of a merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration
func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestApprovalConfigurationOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mergeRequestIID)

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(MergeRequest)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// ChangeMergeRequestAllowedApproversOptions represents the available
// ChangeMergeRequestAllowedApprovers() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request
type ChangeMergeRequestAllowedApproversOptions struct {
	ApproverIDs      []int `url:"approver_ids" json:"approver_ids"`
	ApproverGroupIDs []int `url:"approver_group_ids" json:"approver_group_ids"`
}

// ChangeAllowedApprovers updates the approvers for a merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request
func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestAllowedApproversOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approvers", pathEscape(project), mergeRequestIID)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(MergeRequest)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// GetApprovalRules requests information about a merge request’s approval rules
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules
func (s *MergeRequestApprovalsService) GetApprovalRules(pid interface{}, mergeRequestIID int, options ...OptionFunc) ([]*MergeRequestApprovalRule, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", pathEscape(project), mergeRequestIID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var par []*MergeRequestApprovalRule
	resp, err := s.client.Do(req, &par)
	if err != nil {
		return nil, resp, err
	}

	return par, resp, err
}

// CreateMergeRequestApprovalRuleOptions represents the available CreateApprovalRule()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule
type CreateMergeRequestApprovalRuleOptions struct {
	Name                  *string `url:"name,omitempty" json:"name,omitempty"`
	ApprovalsRequired     *int    `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
	ApprovalProjectRuleID *int    `url:"approval_project_rule_id,omitempty" json:"approval_project_rule_id,omitempty"`
	UserIDs               []int   `url:"user_ids,omitempty" json:"user_ids,omitempty"`
	GroupIDs              []int   `url:"group_ids,omitempty" json:"group_ids,omitempty"`
}

// CreateApprovalRule creates a new MR level approval rule.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule
func (s *MergeRequestApprovalsService) CreateApprovalRule(pid interface{}, mergeRequestIID int, opt *CreateMergeRequestApprovalRuleOptions, options ...OptionFunc) (*MergeRequestApprovalRule, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", pathEscape(project), mergeRequestIID)

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	par := new(MergeRequestApprovalRule)
	resp, err := s.client.Do(req, &par)
	if err != nil {
		return nil, resp, err
	}

	return par, resp, err
}

// UpdateMergeRequestApprovalRuleOptions represents the available UpdateApprovalRule()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule
type UpdateMergeRequestApprovalRuleOptions struct {
	Name              *string `url:"name,omitempty" json:"name,omitempty"`
	ApprovalsRequired *int    `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
	UserIDs           []int   `url:"user_ids,omitempty" json:"user_ids,omitempty"`
	GroupIDs          []int   `url:"group_ids,omitempty" json:"group_ids,omitempty"`
}

// UpdateApprovalRule updates an existing approval rule with new options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule
func (s *MergeRequestApprovalsService) UpdateApprovalRule(pid interface{}, mergeRequestIID int, approvalRule int, opt *UpdateMergeRequestApprovalRuleOptions, options ...OptionFunc) (*MergeRequestApprovalRule, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", pathEscape(project), mergeRequestIID, approvalRule)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	par := new(MergeRequestApprovalRule)
	resp, err := s.client.Do(req, &par)
	if err != nil {
		return nil, resp, err
	}

	return par, resp, err
}

// DeleteApprovalRule deletes a mr level approval rule.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#delete-merge-request-level-rule
func (s *MergeRequestApprovalsService) DeleteApprovalRule(pid interface{}, mergeRequestIID int, approvalRule int, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", pathEscape(project), mergeRequestIID, approvalRule)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<|endoftext|>


<commit_before>package filer2

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"sync"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
)

type ChunkReadAt struct {
	masterClient *wdclient.MasterClient
	chunkViews   []*ChunkView
	buffer       []byte
	bufferOffset int64
	lookupFileId func(fileId string) (targetUrl string, err error)
	readerLock   sync.Mutex

	chunkCache *chunk_cache.ChunkCache
}

// var _ = io.ReaderAt(&ChunkReadAt{})

func NewChunkReaderAtFromClient(filerClient filer_pb.FilerClient, chunkViews []*ChunkView, chunkCache *chunk_cache.ChunkCache) *ChunkReadAt {

	return &ChunkReadAt{
		chunkViews: chunkViews,
		lookupFileId: func(fileId string) (targetUrl string, err error) {
			err = filerClient.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
				vid := VolumeId(fileId)
				resp, err := client.LookupVolume(context.Background(), &filer_pb.LookupVolumeRequest{
					VolumeIds: []string{vid},
				})
				if err != nil {
					return err
				}

				locations := resp.LocationsMap[vid]
				if locations == nil || len(locations.Locations) == 0 {
					glog.V(0).Infof("failed to locate %s", fileId)
					return fmt.Errorf("failed to locate %s", fileId)
				}

				volumeServerAddress := filerClient.AdjustedUrl(locations.Locations[0].Url)

				targetUrl = fmt.Sprintf("http://%s/%s", volumeServerAddress, fileId)

				return nil
			})
			return
		},
		bufferOffset: -1,
		chunkCache:   chunkCache,
	}
}

func (c *ChunkReadAt) ReadAt(p []byte, offset int64) (n int, err error) {

	c.readerLock.Lock()
	defer c.readerLock.Unlock()

	for n < len(p) && err == nil {
		readCount, readErr := c.doReadAt(p[n:], offset+int64(n))
		n += readCount
		err = readErr
		if readCount == 0 {
			return n, nil
		}
	}
	return
}

func (c *ChunkReadAt) doReadAt(p []byte, offset int64) (n int, err error) {

	var found bool
	for _, chunk := range c.chunkViews {
		if chunk.LogicOffset <= offset && offset < chunk.LogicOffset+int64(chunk.Size) {
			found = true
			if c.bufferOffset != chunk.LogicOffset {
				c.buffer, err = c.fetchChunkData(chunk)
				c.bufferOffset = chunk.LogicOffset
			}
			break
		}
	}
	if !found {
		return 0, io.EOF
	}

	n = copy(p, c.buffer[offset-c.bufferOffset:])

	// fmt.Printf("> doReadAt [%d,%d), buffer:[%d,%d)\n", offset, offset+int64(n), c.bufferOffset, c.bufferOffset+int64(len(c.buffer)))

	return

}

func (c *ChunkReadAt) fetchChunkData(chunkView *ChunkView) (data []byte, err error) {

	// fmt.Printf("fetching %s [%d,%d)\n", chunkView.FileId, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size))

	chunkData := c.chunkCache.GetChunk(chunkView.FileId)
	if chunkData != nil {
		glog.V(3).Infof("cache hit %s [%d,%d)", chunkView.FileId, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size))
	} else {
		chunkData, err = c.doFetchFullChunkData(chunkView.FileId, chunkView.CipherKey, chunkView.IsGzipped)
		if err != nil {
			return nil, err
		}
	}

	if int64(len(chunkData)) < chunkView.Offset+int64(chunkView.Size) {
		return nil, fmt.Errorf("unexpected larger chunkView [%d,%d) than chunk %d", chunkView.Offset, chunkView.Offset+int64(chunkView.Size), len(chunkData))
	}

	data = chunkData[chunkView.Offset : chunkView.Offset+int64(chunkView.Size)]

	c.chunkCache.SetChunk(chunkView.FileId, chunkData)

	return data, nil
}

func (c *ChunkReadAt) doFetchFullChunkData(fileId string, cipherKey []byte, isGzipped bool) ([]byte, error) {

	urlString, err := c.lookupFileId(fileId)
	if err != nil {
		glog.V(1).Infof("operation LookupFileId %s failed, err: %v", fileId, err)
		return nil, err
	}
	var buffer bytes.Buffer
	err = util.ReadUrlAsStream(urlString, cipherKey, isGzipped, true, 0, 0, func(data []byte) {
		buffer.Write(data)
	})
	if err != nil {
		glog.V(1).Infof("read %s failed, err: %v", fileId, err)
		return nil, err
	}

	return buffer.Bytes(), nil
}
<commit_msg>a little optimization<commit_after>package filer2

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"sync"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
)

type ChunkReadAt struct {
	masterClient *wdclient.MasterClient
	chunkViews   []*ChunkView
	buffer       []byte
	bufferOffset int64
	lookupFileId func(fileId string) (targetUrl string, err error)
	readerLock   sync.Mutex

	chunkCache *chunk_cache.ChunkCache
}

// var _ = io.ReaderAt(&ChunkReadAt{})

func NewChunkReaderAtFromClient(filerClient filer_pb.FilerClient, chunkViews []*ChunkView, chunkCache *chunk_cache.ChunkCache) *ChunkReadAt {

	return &ChunkReadAt{
		chunkViews: chunkViews,
		lookupFileId: func(fileId string) (targetUrl string, err error) {
			err = filerClient.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
				vid := VolumeId(fileId)
				resp, err := client.LookupVolume(context.Background(), &filer_pb.LookupVolumeRequest{
					VolumeIds: []string{vid},
				})
				if err != nil {
					return err
				}

				locations := resp.LocationsMap[vid]
				if locations == nil || len(locations.Locations) == 0 {
					glog.V(0).Infof("failed to locate %s", fileId)
					return fmt.Errorf("failed to locate %s", fileId)
				}

				volumeServerAddress := filerClient.AdjustedUrl(locations.Locations[0].Url)

				targetUrl = fmt.Sprintf("http://%s/%s", volumeServerAddress, fileId)

				return nil
			})
			return
		},
		bufferOffset: -1,
		chunkCache:   chunkCache,
	}
}

func (c *ChunkReadAt) ReadAt(p []byte, offset int64) (n int, err error) {

	c.readerLock.Lock()
	defer c.readerLock.Unlock()

	for n < len(p) && err == nil {
		readCount, readErr := c.doReadAt(p[n:], offset+int64(n))
		n += readCount
		err = readErr
		if readCount == 0 {
			return n, nil
		}
	}
	return
}

func (c *ChunkReadAt) doReadAt(p []byte, offset int64) (n int, err error) {

	var found bool
	for _, chunk := range c.chunkViews {
		if chunk.LogicOffset <= offset && offset < chunk.LogicOffset+int64(chunk.Size) {
			found = true
			if c.bufferOffset != chunk.LogicOffset {
				c.buffer, err = c.fetchChunkData(chunk)
				c.bufferOffset = chunk.LogicOffset
			}
			break
		}
	}
	if !found {
		return 0, io.EOF
	}

	n = copy(p, c.buffer[offset-c.bufferOffset:])

	// fmt.Printf("> doReadAt [%d,%d), buffer:[%d,%d)\n", offset, offset+int64(n), c.bufferOffset, c.bufferOffset+int64(len(c.buffer)))

	return

}

func (c *ChunkReadAt) fetchChunkData(chunkView *ChunkView) (data []byte, err error) {

	// fmt.Printf("fetching %s [%d,%d)\n", chunkView.FileId, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size))

	hasDataInCache := false
	chunkData := c.chunkCache.GetChunk(chunkView.FileId)
	if chunkData != nil {
		glog.V(3).Infof("cache hit %s [%d,%d)", chunkView.FileId, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size))
		hasDataInCache = true
	} else {
		chunkData, err = c.doFetchFullChunkData(chunkView.FileId, chunkView.CipherKey, chunkView.IsGzipped)
		if err != nil {
			return nil, err
		}
	}

	if int64(len(chunkData)) < chunkView.Offset+int64(chunkView.Size) {
		return nil, fmt.Errorf("unexpected larger chunkView [%d,%d) than chunk %d", chunkView.Offset, chunkView.Offset+int64(chunkView.Size), len(chunkData))
	}

	data = chunkData[chunkView.Offset : chunkView.Offset+int64(chunkView.Size)]

	if !hasDataInCache {
		c.chunkCache.SetChunk(chunkView.FileId, chunkData)
	}

	return data, nil
}

func (c *ChunkReadAt) doFetchFullChunkData(fileId string, cipherKey []byte, isGzipped bool) ([]byte, error) {

	urlString, err := c.lookupFileId(fileId)
	if err != nil {
		glog.V(1).Infof("operation LookupFileId %s failed, err: %v", fileId, err)
		return nil, err
	}
	var buffer bytes.Buffer
	err = util.ReadUrlAsStream(urlString, cipherKey, isGzipped, true, 0, 0, func(data []byte) {
		buffer.Write(data)
	})
	if err != nil {
		glog.V(1).Infof("read %s failed, err: %v", fileId, err)
		return nil, err
	}

	return buffer.Bytes(), nil
}
<|endoftext|>


<commit_before>package moh

import (
	"log"
	"net"
	"net/http"
)

type MessagingServer struct {
	listener net.Listener
	Mux      *http.ServeMux
}

// NewClosableServer returns a pointer to a new ClosableServer.
// After creation, handlers can be registered on Mux and the server
// can be started with Serve() function. Then, you can close it with Close().
func NewClosableServer(addr string) (*MessagingServer, error) {
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}

	return &MessagingServer{
		listener: l,
		Mux:      http.NewServeMux(),
	}, nil
}

func (s *MessagingServer) Serve() {
	http.Serve(s.listener, s.Mux)
	log.Println("Serving has finished")
}

func (s *MessagingServer) Close() error {
	return s.listener.Close()
}
<commit_msg>implement Addr method for MessagingServer<commit_after>package moh

import (
	"log"
	"net"
	"net/http"
)

type MessagingServer struct {
	listener net.Listener
	Mux      *http.ServeMux
}

// NewClosableServer returns a pointer to a new ClosableServer.
// After creation, handlers can be registered on Mux and the server
// can be started with Serve() function. Then, you can close it with Close().
func NewClosableServer(addr string) (*MessagingServer, error) {
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}

	return &MessagingServer{
		listener: l,
		Mux:      http.NewServeMux(),
	}, nil
}

func (s *MessagingServer) Serve() {
	http.Serve(s.listener, s.Mux)
	log.Println("Serving has finished")
}

func (s *MessagingServer) Close() error {
	return s.listener.Close()
}

func (s *MessagingServer) Addr() net.Addr {
	return s.listener.Addr()
}
<|endoftext|>