<commit_before>package command

import (
	"flag"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/chekun/spore/spored/env"
	"github.com/chekun/spore/spored/schema"
	"github.com/mitchellh/cli"
	"github.com/polaris1119/times"
)

// StatCommand Stat Command
type StatCommand struct {
	UI *cli.BasicUi
}

// Help Stat Command Help
func (c *StatCommand) Help() string {
	helpText := `
Usage: spored serve [options] ...

  Spored Server.

Options:

  -config=config.yml   Configuration file to use.
  -env=development     Environment.
  -start=2012-02-02    Start Date.
  -end=2015-05-05      End Date.
`

	return strings.TrimSpace(helpText)
}

// Synopsis Serve Command Synopsis
func (c *StatCommand) Synopsis() string {
	return "Spored Stat"
}

// Run Stat Command Implementation
func (c *StatCommand) Run(args []string) int {

	var startDate string
	var endDate string

	cmdFlags := flag.NewFlagSet("stat", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.UI.Output(c.Help()) }
	env.ConfigFlags(cmdFlags)

	cmdFlags.StringVar(&startDate, "start", "", "Start Date.")
	cmdFlags.StringVar(&endDate, "end", "", "End Date.")

	if err := cmdFlags.Parse(args); err != nil {
		fmt.Println("Could not parse config: ", err)
		return 1
	}

	environment, err := env.GetEnvironment()
	if err != nil {
		fmt.Println("Could not parse config ", err)
		return 1
	}

	mysql, err = env.GetConnection(environment)
	if err != nil {
		fmt.Println("Could not Get DB Connection: ", err)
		return 1
	}

	startTime := time.Now().Add(time.Hour * -24)
	endTime := time.Now().Add(time.Hour * -24)

	if startDate != "" {
		startTime = parseDate(startDate)
	}

	if endDate != "" {
		endTime = parseDate(endDate).Add(time.Hour * 24)
	}

	fixPostsWithNoGroup()

	for startTime.Before(endTime) {

		currentDate := times.Format("Y-m-d", startTime)

		fmt.Println("Running date: ", currentDate, " ...")

		//loop users
		statUsers(currentDate)
		//loop groups
		statGroups(currentDate)

		startTime = startTime.Add(time.Hour * 24)
	}

	return 0
}

func fixPostsWithNoGroup() {
	sql := "SELECT id, thread_id FROM posts WHERE group_id=0"
	stmt, _ := mysql.Prepare(sql)
	defer stmt.Close()

	rows, _ := stmt.Query()
	defer rows.Close()

	for rows.Next() {
		var postID, threadID, groupID int
		rows.Scan(&postID, &threadID)
		sql = "SELECT group_id FROM threads WHERE id = ?"
		stmt, _ = mysql.Prepare(sql)
		stmt.QueryRow(threadID).Scan(&groupID)
		sql = "UPDATE posts SET group_id = ? WHERE id = ?"
		stmt, _ = mysql.Prepare(sql)
		stmt.Exec(groupID, postID)
		stmt.Close()
	}
}

func statGroups(date string) {
	dateStart := date + " 00:00:00"
	dateEnd := date + " 23:59:59"

	sql := "SELECT id FROM groups ORDER BY id ASC"
	stmt, _ := mysql.Prepare(sql)
	defer stmt.Close()
	rows, _ := stmt.Query()
	defer rows.Close()

	for rows.Next() {
		var groupID int
		rows.Scan(&groupID)
		//count threads
		sql = "SELECT count(*) FROM threads WHERE group_id=? AND created_at >= ? AND created_at <= ?"
		stmt, _ = mysql.Prepare(sql)
		var threadsCount int
		stmt.QueryRow(groupID, dateStart, dateEnd).Scan(&threadsCount)
		//count posts
		sql = "SELECT count(*) FROM posts WHERE group_id=? AND created_at >= ? AND created_at <= ?"
		stmt, _ = mysql.Prepare(sql)
		var postsCount int
		stmt.QueryRow(groupID, dateStart, dateEnd).Scan(&postsCount)
		//count groups
		sql = `
		SELECT count(*) FROM (
			SELECT distinct user_id FROM (
				SELECT user_id FROM threads t WHERE group_id=? AND created_at >= ? AND created_at <= ?
				UNION
				SELECT user_id FROM posts p WHERE group_id=? AND created_at >= ? AND created_at <= ?
			) tp
		) tptp
		`
		var groupsCount int
		stmt, _ = mysql.Prepare(sql)
		stmt.QueryRow(groupID, dateStart, dateEnd, groupID, dateStart, dateEnd).Scan(&groupsCount)
		//@todo group attachments

		saveStat(date, groupID, schema.OwnerGroup, schema.StatThreadsPerday, threadsCount)
		saveStat(date, groupID, schema.OwnerGroup, schema.StatPostsPerday, postsCount)
		saveStat(date, groupID, schema.OwnerGroup, schema.StatLivesPerday, groupsCount)

		//Update Total
		sql = "SELECT stat_type, SUM(stat_value) AS stat_value FROM stats WHERE owner_type=? AND owner_id=?  AND stat_type IN (?, ?, ?) GROUP BY stat_type"
		stmt, _ = mysql.Prepare(sql)
		totalRows, _ := stmt.Query(schema.OwnerGroup, groupID, schema.StatThreadsPerday, schema.StatPostsPerday, schema.StatLivesPerday)
		defer totalRows.Close()

		for totalRows.Next() {
			var statType, statValue int
			totalRows.Scan(&statType, &statValue)
			saveStat(fmt.Sprintf("1970-01-0%d", schema.Stat(statType)+1), groupID, schema.OwnerGroup, schema.Stat(statType)+1, statValue)
		}
	}
}

func statUsers(date string) {

	dateStart := date + " 00:00:00"
	dateEnd := date + " 23:59:59"

	sql := "SELECT id FROM users ORDER BY id ASC"
	stmt, _ := mysql.Prepare(sql)
	defer stmt.Close()
	rows, _ := stmt.Query()
	defer rows.Close()

	for rows.Next() {
		var userID int
		rows.Scan(&userID)
		//count threads
		sql = "SELECT count(*) FROM threads WHERE user_id=? AND created_at >= ? AND created_at <= ?"
		stmt, _ = mysql.Prepare(sql)
		var threadsCount int
		stmt.QueryRow(userID, dateStart, dateEnd).Scan(&threadsCount)
		//count posts
		sql = "SELECT count(*) FROM posts WHERE user_id=? AND created_at >= ? AND created_at <= ?"
		stmt, _ = mysql.Prepare(sql)
		var postsCount int
		stmt.QueryRow(userID, dateStart, dateEnd).Scan(&postsCount)
		//count groups
		sql = `
		SELECT count(*) FROM (
			SELECT distinct group_id FROM (
				SELECT group_id FROM threads t WHERE user_id=? AND created_at >= ? AND created_at <= ?
				UNION
				SELECT group_id FROM posts p WHERE user_id=? AND created_at >= ? AND created_at <= ?
			) tp
		) tptp
		`
		var groupsCount int
		stmt, _ = mysql.Prepare(sql)
		stmt.QueryRow(userID, dateStart, dateEnd, userID, dateStart, dateEnd).Scan(&groupsCount)
		//count attachments
		sql = "SELECT count(*) FROM attachments WHERE user_id = ?"
		stmt, _ = mysql.Prepare(sql)
		var attachmentsCount int
		stmt.QueryRow(userID).Scan(&attachmentsCount)

		saveStat(date, userID, schema.OwnerUser, schema.StatThreadsPerday, threadsCount)
		saveStat(date, userID, schema.OwnerUser, schema.StatPostsPerday, postsCount)
		saveStat(date, userID, schema.OwnerUser, schema.StatLivesPerday, groupsCount)
		saveStat(date, userID, schema.OwnerUser, schema.StatAttachmentsPerday, attachmentsCount)

		//Update Total
		sql = "SELECT stat_type, SUM(stat_value) AS stat_value FROM stats WHERE owner_type=? AND owner_id=?  AND stat_type IN (?, ?, ?, ?) GROUP BY stat_type"
		stmt, _ = mysql.Prepare(sql)
		totalRows, _ := stmt.Query(schema.OwnerUser, userID, schema.StatThreadsPerday, schema.StatPostsPerday, schema.StatLivesPerday, schema.StatAttachmentsPerday)
		defer totalRows.Close()

		for totalRows.Next() {
			var statType, statValue int
			totalRows.Scan(&statType, &statValue)
			saveStat(fmt.Sprintf("1970-01-0%d", schema.Stat(statType)+1), userID, schema.OwnerUser, schema.Stat(statType)+1, statValue)
		}
	}
}

func saveStat(date string, ownerID int, ownerType schema.Stat, valueType schema.Stat, value int) {
	sql := "REPLACE INTO stats VALUES(?, ?, ?, ?, ?)"
	stmt, _ := mysql.Prepare(sql)
	stmt.Exec(ownerID, ownerType, date, valueType, value)
	stmt.Close()
}

func parseDate(dateString string) time.Time {
	segments := strings.Split(dateString, "-")
	year, _ := strconv.Atoi(segments[0])
	month, _ := strconv.Atoi(segments[1])
	day, _ := strconv.Atoi(segments[2])
	timeZone, _ := time.LoadLocation("PRC")
	date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, timeZone)

	return date
}
<commit_msg>fix typo<commit_after>package command

import (
	"flag"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/chekun/spore/spored/env"
	"github.com/chekun/spore/spored/schema"
	"github.com/mitchellh/cli"
	"github.com/polaris1119/times"
)

// StatCommand Stat Command
type StatCommand struct {
	UI *cli.BasicUi
}

// Help Stat Command Help
func (c *StatCommand) Help() string {
	helpText := `
Usage: spored serve [options] ...

  Spored Stat.

Options:

  -config=config.yml   Configuration file to use.
  -env=development     Environment.
  -start=2012-02-02    Start Date.
  -end=2015-05-05      End Date.
`

	return strings.TrimSpace(helpText)
}

// Synopsis Serve Command Synopsis
func (c *StatCommand) Synopsis() string {
	return "Spored Stat"
}

// Run Stat Command Implementation
func (c *StatCommand) Run(args []string) int {

	var startDate string
	var endDate string

	cmdFlags := flag.NewFlagSet("stat", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.UI.Output(c.Help()) }
	env.ConfigFlags(cmdFlags)

	cmdFlags.StringVar(&startDate, "start", "", "Start Date.")
	cmdFlags.StringVar(&endDate, "end", "", "End Date.")

	if err := cmdFlags.Parse(args); err != nil {
		fmt.Println("Could not parse config: ", err)
		return 1
	}

	environment, err := env.GetEnvironment()
	if err != nil {
		fmt.Println("Could not parse config ", err)
		return 1
	}

	mysql, err = env.GetConnection(environment)
	if err != nil {
		fmt.Println("Could not Get DB Connection: ", err)
		return 1
	}

	startTime := time.Now().Add(time.Hour * -24)
	endTime := time.Now().Add(time.Hour * -24)

	if startDate != "" {
		startTime = parseDate(startDate)
	}

	if endDate != "" {
		endTime = parseDate(endDate).Add(time.Hour * 24)
	}

	fixPostsWithNoGroup()

	for startTime.Before(endTime) {

		currentDate := times.Format("Y-m-d", startTime)

		fmt.Println("Running date: ", currentDate, " ...")

		//loop users
		statUsers(currentDate)
		//loop groups
		statGroups(currentDate)

		startTime = startTime.Add(time.Hour * 24)
	}

	return 0
}

func fixPostsWithNoGroup() {
	sql := "SELECT id, thread_id FROM posts WHERE group_id=0"
	stmt, _ := mysql.Prepare(sql)
	defer stmt.Close()

	rows, _ := stmt.Query()
	defer rows.Close()

	for rows.Next() {
		var postID, threadID, groupID int
		rows.Scan(&postID, &threadID)
		sql = "SELECT group_id FROM threads WHERE id = ?"
		stmt, _ = mysql.Prepare(sql)
		stmt.QueryRow(threadID).Scan(&groupID)
		sql = "UPDATE posts SET group_id = ? WHERE id = ?"
		stmt, _ = mysql.Prepare(sql)
		stmt.Exec(groupID, postID)
		stmt.Close()
	}
}

func statGroups(date string) {
	dateStart := date + " 00:00:00"
	dateEnd := date + " 23:59:59"

	sql := "SELECT id FROM groups ORDER BY id ASC"
	stmt, _ := mysql.Prepare(sql)
	defer stmt.Close()
	rows, _ := stmt.Query()
	defer rows.Close()

	for rows.Next() {
		var groupID int
		rows.Scan(&groupID)
		//count threads
		sql = "SELECT count(*) FROM threads WHERE group_id=? AND created_at >= ? AND created_at <= ?"
		stmt, _ = mysql.Prepare(sql)
		var threadsCount int
		stmt.QueryRow(groupID, dateStart, dateEnd).Scan(&threadsCount)
		//count posts
		sql = "SELECT count(*) FROM posts WHERE group_id=? AND created_at >= ? AND created_at <= ?"
		stmt, _ = mysql.Prepare(sql)
		var postsCount int
		stmt.QueryRow(groupID, dateStart, dateEnd).Scan(&postsCount)
		//count groups
		sql = `
		SELECT count(*) FROM (
			SELECT distinct user_id FROM (
				SELECT user_id FROM threads t WHERE group_id=? AND created_at >= ? AND created_at <= ?
				UNION
				SELECT user_id FROM posts p WHERE group_id=? AND created_at >= ? AND created_at <= ?
			) tp
		) tptp
		`
		var groupsCount int
		stmt, _ = mysql.Prepare(sql)
		stmt.QueryRow(groupID, dateStart, dateEnd, groupID, dateStart, dateEnd).Scan(&groupsCount)
		//@todo group attachments

		saveStat(date, groupID, schema.OwnerGroup, schema.StatThreadsPerday, threadsCount)
		saveStat(date, groupID, schema.OwnerGroup, schema.StatPostsPerday, postsCount)
		saveStat(date, groupID, schema.OwnerGroup, schema.StatLivesPerday, groupsCount)

		//Update Total
		sql = "SELECT stat_type, SUM(stat_value) AS stat_value FROM stats WHERE owner_type=? AND owner_id=?  AND stat_type IN (?, ?, ?) GROUP BY stat_type"
		stmt, _ = mysql.Prepare(sql)
		totalRows, _ := stmt.Query(schema.OwnerGroup, groupID, schema.StatThreadsPerday, schema.StatPostsPerday, schema.StatLivesPerday)
		defer totalRows.Close()

		for totalRows.Next() {
			var statType, statValue int
			totalRows.Scan(&statType, &statValue)
			saveStat(fmt.Sprintf("1970-01-0%d", schema.Stat(statType)+1), groupID, schema.OwnerGroup, schema.Stat(statType)+1, statValue)
		}
	}
}

func statUsers(date string) {

	dateStart := date + " 00:00:00"
	dateEnd := date + " 23:59:59"

	sql := "SELECT id FROM users ORDER BY id ASC"
	stmt, _ := mysql.Prepare(sql)
	defer stmt.Close()
	rows, _ := stmt.Query()
	defer rows.Close()

	for rows.Next() {
		var userID int
		rows.Scan(&userID)
		//count threads
		sql = "SELECT count(*) FROM threads WHERE user_id=? AND created_at >= ? AND created_at <= ?"
		stmt, _ = mysql.Prepare(sql)
		var threadsCount int
		stmt.QueryRow(userID, dateStart, dateEnd).Scan(&threadsCount)
		//count posts
		sql = "SELECT count(*) FROM posts WHERE user_id=? AND created_at >= ? AND created_at <= ?"
		stmt, _ = mysql.Prepare(sql)
		var postsCount int
		stmt.QueryRow(userID, dateStart, dateEnd).Scan(&postsCount)
		//count groups
		sql = `
		SELECT count(*) FROM (
			SELECT distinct group_id FROM (
				SELECT group_id FROM threads t WHERE user_id=? AND created_at >= ? AND created_at <= ?
				UNION
				SELECT group_id FROM posts p WHERE user_id=? AND created_at >= ? AND created_at <= ?
			) tp
		) tptp
		`
		var groupsCount int
		stmt, _ = mysql.Prepare(sql)
		stmt.QueryRow(userID, dateStart, dateEnd, userID, dateStart, dateEnd).Scan(&groupsCount)
		//count attachments
		sql = "SELECT count(*) FROM attachments WHERE user_id = ?"
		stmt, _ = mysql.Prepare(sql)
		var attachmentsCount int
		stmt.QueryRow(userID).Scan(&attachmentsCount)

		saveStat(date, userID, schema.OwnerUser, schema.StatThreadsPerday, threadsCount)
		saveStat(date, userID, schema.OwnerUser, schema.StatPostsPerday, postsCount)
		saveStat(date, userID, schema.OwnerUser, schema.StatLivesPerday, groupsCount)
		saveStat(date, userID, schema.OwnerUser, schema.StatAttachmentsPerday, attachmentsCount)

		//Update Total
		sql = "SELECT stat_type, SUM(stat_value) AS stat_value FROM stats WHERE owner_type=? AND owner_id=?  AND stat_type IN (?, ?, ?, ?) GROUP BY stat_type"
		stmt, _ = mysql.Prepare(sql)
		totalRows, _ := stmt.Query(schema.OwnerUser, userID, schema.StatThreadsPerday, schema.StatPostsPerday, schema.StatLivesPerday, schema.StatAttachmentsPerday)
		defer totalRows.Close()

		for totalRows.Next() {
			var statType, statValue int
			totalRows.Scan(&statType, &statValue)
			saveStat(fmt.Sprintf("1970-01-0%d", schema.Stat(statType)+1), userID, schema.OwnerUser, schema.Stat(statType)+1, statValue)
		}
	}
}

func saveStat(date string, ownerID int, ownerType schema.Stat, valueType schema.Stat, value int) {
	sql := "REPLACE INTO stats VALUES(?, ?, ?, ?, ?)"
	stmt, _ := mysql.Prepare(sql)
	stmt.Exec(ownerID, ownerType, date, valueType, value)
	stmt.Close()
}

func parseDate(dateString string) time.Time {
	segments := strings.Split(dateString, "-")
	year, _ := strconv.Atoi(segments[0])
	month, _ := strconv.Atoi(segments[1])
	day, _ := strconv.Atoi(segments[2])
	timeZone, _ := time.LoadLocation("PRC")
	date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, timeZone)

	return date
}
<|endoftext|>


<commit_before>package moviectl

import (
	"context"
	"time"

	"github.com/gilcrest/errors"
	"github.com/gilcrest/go-api-basic/datastore"
	"github.com/gilcrest/go-api-basic/datastore/movieds"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/movie"
	"github.com/rs/zerolog"
)

// AddMovieRequest is the request struct
type AddMovieRequest struct {
	Title    string `json:"Title"`
	Year     int    `json:"Year"`
	Rated    string `json:"Rated"`
	Released string `json:"ReleaseDate"`
	RunTime  int    `json:"RunTime"`
	Director string `json:"Director"`
	Writer   string `json:"Writer"`
}

// AddMovieResponse is the response struct
type AddMovieResponse struct {
	ID              string `json:"ID"`
	Title           string `json:"Title"`
	Year            int    `json:"Year"`
	Rated           string `json:"Rated"`
	Released        string `json:"ReleaseDate"`
	RunTime         int    `json:"RunTime"`
	Director        string `json:"Director"`
	Writer          string `json:"Writer"`
	CreateTimestamp string `json:"CreateTimestamp"`
}

type addMovieController struct {
	Request *AddMovieRequest
	MovieDS movieds.MovieDS
}

func (amc addMovieController) add(ctx context.Context) (*AddMovieResponse, error) {
	const op errs.Op = "domain/movie/AddMovie"

	m, err := provideMovie(amc.Request)
	if err != nil {
		return nil, errs.E(op, err)
	}

	err = m.Add(ctx)
	if err != nil {
		return nil, errs.E(err)
	}

	aud := new(audit.Audit)

	err = amc.MovieDS.Store(ctx, m, aud)
	if err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	return provideAddMovieResponse(m, aud), nil
}

func provideAddMovieController(r *AddMovieRequest, ds movieds.MovieDS) *addMovieController {
	return &addMovieController{Request: r, MovieDS: ds}
}

// provideAddMovieResponse is an initializer for AddMovieResponse
func provideAddMovieResponse(m *movie.Movie, a *audit.Audit) *AddMovieResponse {
	return &AddMovieResponse{
		ID:              m.ID,
		Title:           m.Title,
		Year:            m.Year,
		Rated:           m.Rated,
		Released:        m.Released.Format(time.RFC3339),
		RunTime:         m.RunTime,
		Director:        m.Director,
		Writer:          m.Writer,
		CreateTimestamp: a.CreateTimestamp.Format(time.RFC3339),
	}
}

// dateFormat is the expected date format for any date fields
// in the request
const dateFormat string = "Jan 02 2006"

// ProvideMovie is an initializer for the Movie struct
func provideMovie(am *AddMovieRequest) (*movie.Movie, error) {
	const op errs.Op = "domain/movie/ProvideMovie"

	t, err := time.Parse(dateFormat, am.Released)
	if err != nil {
		return nil, errs.E(op,
			errs.Validation,
			errs.Code("invalid_date_format"),
			errs.Parameter("ReleaseDate"),
			err)
	}

	return &movie.Movie{
		Title:    am.Title,
		Year:     am.Year,
		Rated:    am.Rated,
		Released: t,
		RunTime:  am.RunTime,
		Director: am.Director,
		Writer:   am.Writer,
	}, nil
}

// AddMovie adds a movie to the catalog.
func AddMovie(ctx context.Context, ds datastore.Datastore, log zerolog.Logger, r *AddMovieRequest) (*AddMovieResponse, error) {
	const op errs.Op = "controller/moviectl/AddMovie"

	err := ds.BeginTx(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	mds, err := movieds.ProvideMovieDS(ds, log)
	if err != nil {
		return nil, errs.E(op, err)
	}

	amc := provideAddMovieController(r, mds)
	resp, err := amc.add(ctx)
	if err != nil {
		return nil, ds.RollbackTx(err)
	}

	if err := ds.CommitTx(); err != nil {
		return nil, errs.E(op, errors.Database, err)
	}

	return resp, nil
}

// Pull client information from Server token and set
// 	createClient, err := apiclient.ViaServerToken(ctx, tx)
// 	if err != nil {
// 		return errors.E(op, errors.Internal, err)
// 	}
// 	m.CreateClient.Number = createClient.Number
// 	m.UpdateClient.Number = createClient.Number
<commit_msg>Fix to remove old errors module<commit_after>package moviectl

import (
	"context"
	"time"

	"github.com/gilcrest/go-api-basic/datastore"
	"github.com/gilcrest/go-api-basic/datastore/movieds"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/movie"
	"github.com/rs/zerolog"
)

// AddMovieRequest is the request struct
type AddMovieRequest struct {
	Title    string `json:"Title"`
	Year     int    `json:"Year"`
	Rated    string `json:"Rated"`
	Released string `json:"ReleaseDate"`
	RunTime  int    `json:"RunTime"`
	Director string `json:"Director"`
	Writer   string `json:"Writer"`
}

// AddMovieResponse is the response struct
type AddMovieResponse struct {
	ID              string `json:"ID"`
	Title           string `json:"Title"`
	Year            int    `json:"Year"`
	Rated           string `json:"Rated"`
	Released        string `json:"ReleaseDate"`
	RunTime         int    `json:"RunTime"`
	Director        string `json:"Director"`
	Writer          string `json:"Writer"`
	CreateTimestamp string `json:"CreateTimestamp"`
}

type addMovieController struct {
	Request *AddMovieRequest
	MovieDS movieds.MovieDS
}

func (amc addMovieController) add(ctx context.Context) (*AddMovieResponse, error) {
	const op errs.Op = "domain/movie/AddMovie"

	m, err := provideMovie(amc.Request)
	if err != nil {
		return nil, errs.E(op, err)
	}

	err = m.Add(ctx)
	if err != nil {
		return nil, errs.E(err)
	}

	aud := new(audit.Audit)

	err = amc.MovieDS.Store(ctx, m, aud)
	if err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	return provideAddMovieResponse(m, aud), nil
}

func provideAddMovieController(r *AddMovieRequest, ds movieds.MovieDS) *addMovieController {
	return &addMovieController{Request: r, MovieDS: ds}
}

// provideAddMovieResponse is an initializer for AddMovieResponse
func provideAddMovieResponse(m *movie.Movie, a *audit.Audit) *AddMovieResponse {
	return &AddMovieResponse{
		ID:              m.ID,
		Title:           m.Title,
		Year:            m.Year,
		Rated:           m.Rated,
		Released:        m.Released.Format(time.RFC3339),
		RunTime:         m.RunTime,
		Director:        m.Director,
		Writer:          m.Writer,
		CreateTimestamp: a.CreateTimestamp.Format(time.RFC3339),
	}
}

// dateFormat is the expected date format for any date fields
// in the request
const dateFormat string = "Jan 02 2006"

// ProvideMovie is an initializer for the Movie struct
func provideMovie(am *AddMovieRequest) (*movie.Movie, error) {
	const op errs.Op = "domain/movie/ProvideMovie"

	t, err := time.Parse(dateFormat, am.Released)
	if err != nil {
		return nil, errs.E(op,
			errs.Validation,
			errs.Code("invalid_date_format"),
			errs.Parameter("ReleaseDate"),
			err)
	}

	return &movie.Movie{
		Title:    am.Title,
		Year:     am.Year,
		Rated:    am.Rated,
		Released: t,
		RunTime:  am.RunTime,
		Director: am.Director,
		Writer:   am.Writer,
	}, nil
}

// AddMovie adds a movie to the catalog.
func AddMovie(ctx context.Context, ds datastore.Datastore, log zerolog.Logger, r *AddMovieRequest) (*AddMovieResponse, error) {
	const op errs.Op = "controller/moviectl/AddMovie"

	err := ds.BeginTx(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	mds, err := movieds.ProvideMovieDS(ds, log)
	if err != nil {
		return nil, errs.E(op, err)
	}

	amc := provideAddMovieController(r, mds)
	resp, err := amc.add(ctx)
	if err != nil {
		return nil, ds.RollbackTx(err)
	}

	if err := ds.CommitTx(); err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	return resp, nil
}

// Pull client information from Server token and set
// 	createClient, err := apiclient.ViaServerToken(ctx, tx)
// 	if err != nil {
// 		return errs.E(op, errs.Internal, err)
// 	}
// 	m.CreateClient.Number = createClient.Number
// 	m.UpdateClient.Number = createClient.Number
<|endoftext|>


<commit_before>package nyb

import (
	"strings"
	"testing"
)

func TestChangeNick(t *testing.T) {
	ln := "aaaaaaaaaaaaaaaa"
	for i := 1; i <= 4; i++ {
		ln = changeNick(ln)
		if got := strings.Count(ln, "_"); got != i {
			t.Errorf("expecting %d _'s got %d, string: %s", i, got, ln)
		}
		if len(ln) != 12+i {
			t.Errorf("expecting lenght %d got %d, string: %s", 12+i, len(ln), ln)
		}
	}
	if ln := changeNick(ln); len(ln) != 12 {
		t.Errorf("expecting lenght 12 got %d, string: %s", len(ln), ln)
	}
	n := "a"
	for i := 1; i <= 15; i++ {
		n = changeNick(n)
		c := strings.Count(n, "_")
		if c != i {
			t.Errorf("expecting %d _'s got %d, string: %s", i, c, n)
		}
	}
	if n := changeNick(n); n != "a" {
		t.Error("nick wasn't 'a'")
	}
}
<commit_msg>cover timeNow<commit_after>package nyb

import (
	"strings"
	"testing"
)

func init() {
	timeNow()
}

func TestChangeNick(t *testing.T) {
	ln := "aaaaaaaaaaaaaaaa"
	for i := 1; i <= 4; i++ {
		ln = changeNick(ln)
		if got := strings.Count(ln, "_"); got != i {
			t.Errorf("expecting %d _'s got %d, string: %s", i, got, ln)
		}
		if len(ln) != 12+i {
			t.Errorf("expecting lenght %d got %d, string: %s", 12+i, len(ln), ln)
		}
	}
	if ln := changeNick(ln); len(ln) != 12 {
		t.Errorf("expecting lenght 12 got %d, string: %s", len(ln), ln)
	}
	n := "a"
	for i := 1; i <= 15; i++ {
		n = changeNick(n)
		c := strings.Count(n, "_")
		if c != i {
			t.Errorf("expecting %d _'s got %d, string: %s", i, c, n)
		}
	}
	if n := changeNick(n); n != "a" {
		t.Error("nick wasn't 'a'")
	}
}
<|endoftext|>


<commit_before>package sarama

import (
	"sync"
	"time"
)

// Offset Manager

// OffsetManager uses Kafka to store and fetch consumed partition offsets.
type OffsetManager interface {
	// ManagePartition creates a PartitionOffsetManager on the given topic/partition. It will
	// return an error if this OffsetManager is already managing the given topic/partition.
	ManagePartition(topic string, partition int32) (PartitionOffsetManager, error)
}

type offsetManager struct {
	client Client
	conf   *Config
	group  string

	lock sync.Mutex
	poms map[string]map[int32]*partitionOffsetManager
	boms map[*Broker]*brokerOffsetManager
}

// NewOffsetManagerFromClient creates a new OffsetManager from the given client.
// It is still necessary to call Close() on the underlying client when finished with the partition manager.
func NewOffsetManagerFromClient(group string, client Client) (OffsetManager, error) {
	// Check that we are not dealing with a closed Client before processing any other arguments
	if client.Closed() {
		return nil, ErrClosedClient
	}

	om := &offsetManager{
		client: client,
		conf:   client.Config(),
		group:  group,
		poms:   make(map[string]map[int32]*partitionOffsetManager),
		boms:   make(map[*Broker]*brokerOffsetManager),
	}

	return om, nil
}

func (om *offsetManager) ManagePartition(topic string, partition int32) (PartitionOffsetManager, error) {
	pom, err := om.newPartitionOffsetManager(topic, partition)
	if err != nil {
		return nil, err
	}

	om.lock.Lock()
	defer om.lock.Unlock()

	topicManagers := om.poms[topic]
	if topicManagers == nil {
		topicManagers = make(map[int32]*partitionOffsetManager)
		om.poms[topic] = topicManagers
	}

	if topicManagers[partition] != nil {
		return nil, ConfigurationError("That topic/partition is already being managed")
	}

	topicManagers[partition] = pom
	return pom, nil
}

func (om *offsetManager) refBrokerOffsetManager(broker *Broker) *brokerOffsetManager {
	om.lock.Lock()
	defer om.lock.Unlock()

	bom := om.boms[broker]
	if bom == nil {
		bom = om.newBrokerOffsetManager(broker)
		om.boms[broker] = bom
	}

	bom.refs++

	return bom
}

func (om *offsetManager) unrefBrokerOffsetManager(bom *brokerOffsetManager) {
	om.lock.Lock()
	defer om.lock.Unlock()

	bom.refs--

	if bom.refs == 0 {
		close(bom.updateSubscriptions)
		if om.boms[bom.broker] == bom {
			delete(om.boms, bom.broker)
		}
	}
}

func (om *offsetManager) abandonBroker(bom *brokerOffsetManager) {
	om.lock.Lock()
	defer om.lock.Unlock()

	delete(om.boms, bom.broker)
}

// Partition Offset Manager

// PartitionOffsetManager uses Kafka to store and fetch consumed partition offsets. You MUST call Close()
// on a partition offset manager to avoid leaks, it will not be garbage-collected automatically when it passes
// out of scope.
type PartitionOffsetManager interface {
	// Offset returns the current offset and metadata according to the manager; this value has not necessarily
	// been flushed to the cluster yet.
	Offset() (int64, string)

	// SetOffset sets the current offset and metadata according to the manager; this value (or a subsequent update)
	// will eventually be flushed to the cluster based on configuration.
	SetOffset(offset int64, metadata string)

	// Errors returns a read channel of errors that occur during offset management, if enabled. By default,
	// errors are logged and not returned over this channel. If you want to implement any custom error
	// handling, set your config's Consumer.Return.Errors setting to true, and read from this channel.
	Errors() <-chan *ConsumerError
	// AsyncClose initiates a shutdown of the PartitionOffsetManager. This method will return immediately,
	// after which you should wait until the 'errors' channel has been drained and closed.
	// It is required to call this function, or Close before a consumer object passes out of scope,
	// as it will otherwise leak memory.  You must call this before calling Close on the underlying
	// client.
	AsyncClose()
	// Close stops the PartitionOffsetManager from managing offsets. It is required to call this function
	// (or AsyncClose) before a PartitionOffsetManager object passes out of scope, as it will otherwise
	// leak memory. You must call this before calling Close on the underlying client.
	Close() error
}

type partitionOffsetManager struct {
	parent    *offsetManager
	topic     string
	partition int32

	lock     sync.Mutex
	offset   int64
	metadata string
	dirty    bool
	clean    chan none
	broker   *brokerOffsetManager

	errors    chan *ConsumerError
	rebalance chan none
	dying     chan none
}

func (om *offsetManager) newPartitionOffsetManager(topic string, partition int32) (*partitionOffsetManager, error) {
	pom := &partitionOffsetManager{
		parent:    om,
		topic:     topic,
		partition: partition,
		clean:     make(chan none),
		errors:    make(chan *ConsumerError, om.conf.ChannelBufferSize),
		rebalance: make(chan none, 1),
		dying:     make(chan none),
	}

	if err := pom.selectBroker(); err != nil {
		return nil, err
	}

	if err := pom.fetchInitialOffset(om.conf.Metadata.Retry.Max); err != nil {
		return nil, err
	}

	go withRecover(pom.mainLoop)

	return pom, nil
}

func (pom *partitionOffsetManager) mainLoop() {
	for {
		select {
		case <-pom.rebalance:
			if err := pom.selectBroker(); err != nil {
				pom.handleError(err)
				pom.rebalance <- none{}
			}
		case <-pom.dying:
			if pom.broker != nil {
				select {
				case <-pom.rebalance:
				case pom.broker.updateSubscriptions <- pom:
				}
				pom.parent.unrefBrokerOffsetManager(pom.broker)
			}
			close(pom.errors)
			return
		}
	}
}

func (pom *partitionOffsetManager) selectBroker() error {
	if pom.broker != nil {
		pom.parent.unrefBrokerOffsetManager(pom.broker)
		pom.broker = nil
	}

	var broker *Broker
	var err error

	if err = pom.parent.client.RefreshCoordinator(pom.parent.group); err != nil {
		return err
	}

	if broker, err = pom.parent.client.Coordinator(pom.parent.group); err != nil {
		return err
	}

	pom.broker = pom.parent.refBrokerOffsetManager(broker)
	pom.broker.updateSubscriptions <- pom
	return nil
}

func (pom *partitionOffsetManager) fetchInitialOffset(retries int) error {
	request := new(OffsetFetchRequest)
	request.Version = 1
	request.ConsumerGroup = pom.parent.group
	request.AddPartition(pom.topic, pom.partition)

	response, err := pom.broker.broker.FetchOffset(request)
	if err != nil {
		return err
	}

	block := response.GetBlock(pom.topic, pom.partition)
	if block == nil {
		return ErrIncompleteResponse
	}

	switch block.Err {
	case ErrNoError:
		pom.offset = block.Offset
		pom.metadata = block.Metadata
		return nil
	case ErrNotCoordinatorForConsumer:
		if retries <= 0 {
			return err
		}
		if err := pom.selectBroker(); err != nil {
			return err
		}
		return pom.fetchInitialOffset(retries - 1)
	case ErrOffsetsLoadInProgress:
		if retries <= 0 {
			return err
		}
		time.Sleep(pom.parent.conf.Metadata.Retry.Backoff)
		return pom.fetchInitialOffset(retries - 1)
	default:
		return err
	}
}

func (pom *partitionOffsetManager) handleError(err error) {
	cErr := &ConsumerError{
		Topic:     pom.topic,
		Partition: pom.partition,
		Err:       err,
	}

	if pom.parent.conf.Consumer.Return.Errors {
		pom.errors <- cErr
	} else {
		Logger.Println(cErr)
	}
}

func (pom *partitionOffsetManager) Errors() <-chan *ConsumerError {
	return pom.errors
}

func (pom *partitionOffsetManager) SetOffset(offset int64, metadata string) {
	pom.lock.Lock()
	defer pom.lock.Unlock()

	pom.offset = offset
	pom.metadata = metadata
	pom.dirty = true
}

func (pom *partitionOffsetManager) updateCommitted(offset int64, metadata string) {
	pom.lock.Lock()
	defer pom.lock.Unlock()

	if pom.offset == offset && pom.metadata == metadata {
		pom.dirty = false

		select {
		case pom.clean <- none{}:
		default:
		}
	}
}

func (pom *partitionOffsetManager) Offset() (int64, string) {
	pom.lock.Lock()
	defer pom.lock.Unlock()

	return pom.offset, pom.metadata
}

func (pom *partitionOffsetManager) AsyncClose() {
	go func() {
		pom.lock.Lock()
		dirty := pom.dirty
		pom.lock.Unlock()

		if dirty {
			<-pom.clean
		}

		close(pom.dying)
	}()
}

func (pom *partitionOffsetManager) Close() error {
	pom.AsyncClose()

	var errors ConsumerErrors
	for err := range pom.errors {
		errors = append(errors, err)
	}

	if len(errors) > 0 {
		return errors
	}
	return nil
}

// Broker Offset Manager

type brokerOffsetManager struct {
	parent              *offsetManager
	broker              *Broker
	timer               *time.Ticker
	updateSubscriptions chan *partitionOffsetManager
	subscriptions       map[*partitionOffsetManager]none
	refs                int
}

func (om *offsetManager) newBrokerOffsetManager(broker *Broker) *brokerOffsetManager {
	bom := &brokerOffsetManager{
		parent:              om,
		broker:              broker,
		timer:               time.NewTicker(om.conf.Consumer.Offsets.CommitInterval),
		updateSubscriptions: make(chan *partitionOffsetManager),
		subscriptions:       make(map[*partitionOffsetManager]none),
	}

	go withRecover(bom.mainLoop)

	return bom
}

func (bom *brokerOffsetManager) mainLoop() {
	for {
		select {
		case <-bom.timer.C:
			bom.flushToBroker()
		case s, ok := <-bom.updateSubscriptions:
			if !ok {
				bom.timer.Stop()
				return
			}
			if _, ok := bom.subscriptions[s]; ok {
				delete(bom.subscriptions, s)
			} else {
				bom.subscriptions[s] = none{}
			}
		}
	}
}

func (bom *brokerOffsetManager) flushToBroker() {
	request := bom.constructRequest()
	response, err := bom.broker.CommitOffset(request)

	if err != nil {
		bom.abort(err)
	}

	for s := range bom.subscriptions {
		var err KError
		var ok bool

		if response.Errors[s.topic] == nil {
			s.handleError(ErrIncompleteResponse)
			delete(bom.subscriptions, s)
			s.rebalance <- none{}
			continue
		}
		if err, ok = response.Errors[s.topic][s.partition]; !ok {
			s.handleError(ErrIncompleteResponse)
			delete(bom.subscriptions, s)
			s.rebalance <- none{}
			continue
		}

		switch err {
		case ErrNoError:
			block := request.blocks[s.topic][s.partition]
			s.updateCommitted(block.offset, block.metadata)
			break
		case ErrUnknownTopicOrPartition, ErrNotLeaderForPartition, ErrLeaderNotAvailable:
			delete(bom.subscriptions, s)
			s.rebalance <- none{}
		default:
			s.handleError(err)
			delete(bom.subscriptions, s)
			s.rebalance <- none{}
		}
	}
}

func (bom *brokerOffsetManager) constructRequest() *OffsetCommitRequest {
	r := &OffsetCommitRequest{
		Version:       1,
		ConsumerGroup: bom.parent.group,
	}
	for s := range bom.subscriptions {
		s.lock.Lock()
		r.AddBlock(s.topic, s.partition, s.offset, 0, s.metadata)
		s.lock.Unlock()
	}
	return r
}

func (bom *brokerOffsetManager) abort(err error) {
	_ = bom.broker.Close() // we don't care about the error this might return, we already have one
	bom.parent.abandonBroker(bom)

	for pom := range bom.subscriptions {
		pom.handleError(err)
		pom.rebalance <- none{}
	}

	for s := range bom.updateSubscriptions {
		if _, ok := bom.subscriptions[s]; !ok {
			s.handleError(err)
			s.rebalance <- none{}
		}
	}
}
<commit_msg>Don't start setting until the initial get succeeds<commit_after>package sarama

import (
	"sync"
	"time"
)

// Offset Manager

// OffsetManager uses Kafka to store and fetch consumed partition offsets.
type OffsetManager interface {
	// ManagePartition creates a PartitionOffsetManager on the given topic/partition. It will
	// return an error if this OffsetManager is already managing the given topic/partition.
	ManagePartition(topic string, partition int32) (PartitionOffsetManager, error)
}

type offsetManager struct {
	client Client
	conf   *Config
	group  string

	lock sync.Mutex
	poms map[string]map[int32]*partitionOffsetManager
	boms map[*Broker]*brokerOffsetManager
}

// NewOffsetManagerFromClient creates a new OffsetManager from the given client.
// It is still necessary to call Close() on the underlying client when finished with the partition manager.
func NewOffsetManagerFromClient(group string, client Client) (OffsetManager, error) {
	// Check that we are not dealing with a closed Client before processing any other arguments
	if client.Closed() {
		return nil, ErrClosedClient
	}

	om := &offsetManager{
		client: client,
		conf:   client.Config(),
		group:  group,
		poms:   make(map[string]map[int32]*partitionOffsetManager),
		boms:   make(map[*Broker]*brokerOffsetManager),
	}

	return om, nil
}

func (om *offsetManager) ManagePartition(topic string, partition int32) (PartitionOffsetManager, error) {
	pom, err := om.newPartitionOffsetManager(topic, partition)
	if err != nil {
		return nil, err
	}

	om.lock.Lock()
	defer om.lock.Unlock()

	topicManagers := om.poms[topic]
	if topicManagers == nil {
		topicManagers = make(map[int32]*partitionOffsetManager)
		om.poms[topic] = topicManagers
	}

	if topicManagers[partition] != nil {
		return nil, ConfigurationError("That topic/partition is already being managed")
	}

	topicManagers[partition] = pom
	return pom, nil
}

func (om *offsetManager) refBrokerOffsetManager(broker *Broker) *brokerOffsetManager {
	om.lock.Lock()
	defer om.lock.Unlock()

	bom := om.boms[broker]
	if bom == nil {
		bom = om.newBrokerOffsetManager(broker)
		om.boms[broker] = bom
	}

	bom.refs++

	return bom
}

func (om *offsetManager) unrefBrokerOffsetManager(bom *brokerOffsetManager) {
	om.lock.Lock()
	defer om.lock.Unlock()

	bom.refs--

	if bom.refs == 0 {
		close(bom.updateSubscriptions)
		if om.boms[bom.broker] == bom {
			delete(om.boms, bom.broker)
		}
	}
}

func (om *offsetManager) abandonBroker(bom *brokerOffsetManager) {
	om.lock.Lock()
	defer om.lock.Unlock()

	delete(om.boms, bom.broker)
}

// Partition Offset Manager

// PartitionOffsetManager uses Kafka to store and fetch consumed partition offsets. You MUST call Close()
// on a partition offset manager to avoid leaks, it will not be garbage-collected automatically when it passes
// out of scope.
type PartitionOffsetManager interface {
	// Offset returns the current offset and metadata according to the manager; this value has not necessarily
	// been flushed to the cluster yet.
	Offset() (int64, string)

	// SetOffset sets the current offset and metadata according to the manager; this value (or a subsequent update)
	// will eventually be flushed to the cluster based on configuration.
	SetOffset(offset int64, metadata string)

	// Errors returns a read channel of errors that occur during offset management, if enabled. By default,
	// errors are logged and not returned over this channel. If you want to implement any custom error
	// handling, set your config's Consumer.Return.Errors setting to true, and read from this channel.
	Errors() <-chan *ConsumerError
	// AsyncClose initiates a shutdown of the PartitionOffsetManager. This method will return immediately,
	// after which you should wait until the 'errors' channel has been drained and closed.
	// It is required to call this function, or Close before a consumer object passes out of scope,
	// as it will otherwise leak memory.  You must call this before calling Close on the underlying
	// client.
	AsyncClose()
	// Close stops the PartitionOffsetManager from managing offsets. It is required to call this function
	// (or AsyncClose) before a PartitionOffsetManager object passes out of scope, as it will otherwise
	// leak memory. You must call this before calling Close on the underlying client.
	Close() error
}

type partitionOffsetManager struct {
	parent    *offsetManager
	topic     string
	partition int32

	lock     sync.Mutex
	offset   int64
	metadata string
	dirty    bool
	clean    chan none
	broker   *brokerOffsetManager

	errors    chan *ConsumerError
	rebalance chan none
	dying     chan none
}

func (om *offsetManager) newPartitionOffsetManager(topic string, partition int32) (*partitionOffsetManager, error) {
	pom := &partitionOffsetManager{
		parent:    om,
		topic:     topic,
		partition: partition,
		clean:     make(chan none),
		errors:    make(chan *ConsumerError, om.conf.ChannelBufferSize),
		rebalance: make(chan none, 1),
		dying:     make(chan none),
	}

	if err := pom.selectBroker(); err != nil {
		return nil, err
	}

	if err := pom.fetchInitialOffset(om.conf.Metadata.Retry.Max); err != nil {
		return nil, err
	}

	pom.broker.updateSubscriptions <- pom

	go withRecover(pom.mainLoop)

	return pom, nil
}

func (pom *partitionOffsetManager) mainLoop() {
	for {
		select {
		case <-pom.rebalance:
			if err := pom.selectBroker(); err != nil {
				pom.handleError(err)
				pom.rebalance <- none{}
			} else {
				pom.broker.updateSubscriptions <- pom
			}
		case <-pom.dying:
			if pom.broker != nil {
				select {
				case <-pom.rebalance:
				case pom.broker.updateSubscriptions <- pom:
				}
				pom.parent.unrefBrokerOffsetManager(pom.broker)
			}
			close(pom.errors)
			return
		}
	}
}

func (pom *partitionOffsetManager) selectBroker() error {
	if pom.broker != nil {
		pom.parent.unrefBrokerOffsetManager(pom.broker)
		pom.broker = nil
	}

	var broker *Broker
	var err error

	if err = pom.parent.client.RefreshCoordinator(pom.parent.group); err != nil {
		return err
	}

	if broker, err = pom.parent.client.Coordinator(pom.parent.group); err != nil {
		return err
	}

	pom.broker = pom.parent.refBrokerOffsetManager(broker)
	return nil
}

func (pom *partitionOffsetManager) fetchInitialOffset(retries int) error {
	request := new(OffsetFetchRequest)
	request.Version = 1
	request.ConsumerGroup = pom.parent.group
	request.AddPartition(pom.topic, pom.partition)

	response, err := pom.broker.broker.FetchOffset(request)
	if err != nil {
		return err
	}

	block := response.GetBlock(pom.topic, pom.partition)
	if block == nil {
		return ErrIncompleteResponse
	}

	switch block.Err {
	case ErrNoError:
		pom.offset = block.Offset
		pom.metadata = block.Metadata
		return nil
	case ErrNotCoordinatorForConsumer:
		if retries <= 0 {
			return err
		}
		if err := pom.selectBroker(); err != nil {
			return err
		}
		return pom.fetchInitialOffset(retries - 1)
	case ErrOffsetsLoadInProgress:
		if retries <= 0 {
			return err
		}
		time.Sleep(pom.parent.conf.Metadata.Retry.Backoff)
		return pom.fetchInitialOffset(retries - 1)
	default:
		return err
	}
}

func (pom *partitionOffsetManager) handleError(err error) {
	cErr := &ConsumerError{
		Topic:     pom.topic,
		Partition: pom.partition,
		Err:       err,
	}

	if pom.parent.conf.Consumer.Return.Errors {
		pom.errors <- cErr
	} else {
		Logger.Println(cErr)
	}
}

func (pom *partitionOffsetManager) Errors() <-chan *ConsumerError {
	return pom.errors
}

func (pom *partitionOffsetManager) SetOffset(offset int64, metadata string) {
	pom.lock.Lock()
	defer pom.lock.Unlock()

	pom.offset = offset
	pom.metadata = metadata
	pom.dirty = true
}

func (pom *partitionOffsetManager) updateCommitted(offset int64, metadata string) {
	pom.lock.Lock()
	defer pom.lock.Unlock()

	if pom.offset == offset && pom.metadata == metadata {
		pom.dirty = false

		select {
		case pom.clean <- none{}:
		default:
		}
	}
}

func (pom *partitionOffsetManager) Offset() (int64, string) {
	pom.lock.Lock()
	defer pom.lock.Unlock()

	return pom.offset, pom.metadata
}

func (pom *partitionOffsetManager) AsyncClose() {
	go func() {
		pom.lock.Lock()
		dirty := pom.dirty
		pom.lock.Unlock()

		if dirty {
			<-pom.clean
		}

		close(pom.dying)
	}()
}

func (pom *partitionOffsetManager) Close() error {
	pom.AsyncClose()

	var errors ConsumerErrors
	for err := range pom.errors {
		errors = append(errors, err)
	}

	if len(errors) > 0 {
		return errors
	}
	return nil
}

// Broker Offset Manager

type brokerOffsetManager struct {
	parent              *offsetManager
	broker              *Broker
	timer               *time.Ticker
	updateSubscriptions chan *partitionOffsetManager
	subscriptions       map[*partitionOffsetManager]none
	refs                int
}

func (om *offsetManager) newBrokerOffsetManager(broker *Broker) *brokerOffsetManager {
	bom := &brokerOffsetManager{
		parent:              om,
		broker:              broker,
		timer:               time.NewTicker(om.conf.Consumer.Offsets.CommitInterval),
		updateSubscriptions: make(chan *partitionOffsetManager),
		subscriptions:       make(map[*partitionOffsetManager]none),
	}

	go withRecover(bom.mainLoop)

	return bom
}

func (bom *brokerOffsetManager) mainLoop() {
	for {
		select {
		case <-bom.timer.C:
			if len(bom.subscriptions) > 0 {
				bom.flushToBroker()
			}
		case s, ok := <-bom.updateSubscriptions:
			if !ok {
				bom.timer.Stop()
				return
			}
			if _, ok := bom.subscriptions[s]; ok {
				delete(bom.subscriptions, s)
			} else {
				bom.subscriptions[s] = none{}
			}
		}
	}
}

func (bom *brokerOffsetManager) flushToBroker() {
	request := bom.constructRequest()
	response, err := bom.broker.CommitOffset(request)

	if err != nil {
		bom.abort(err)
	}

	for s := range bom.subscriptions {
		var err KError
		var ok bool

		if response.Errors[s.topic] == nil {
			s.handleError(ErrIncompleteResponse)
			delete(bom.subscriptions, s)
			s.rebalance <- none{}
			continue
		}
		if err, ok = response.Errors[s.topic][s.partition]; !ok {
			s.handleError(ErrIncompleteResponse)
			delete(bom.subscriptions, s)
			s.rebalance <- none{}
			continue
		}

		switch err {
		case ErrNoError:
			block := request.blocks[s.topic][s.partition]
			s.updateCommitted(block.offset, block.metadata)
			break
		case ErrUnknownTopicOrPartition, ErrNotLeaderForPartition, ErrLeaderNotAvailable:
			delete(bom.subscriptions, s)
			s.rebalance <- none{}
		default:
			s.handleError(err)
			delete(bom.subscriptions, s)
			s.rebalance <- none{}
		}
	}
}

func (bom *brokerOffsetManager) constructRequest() *OffsetCommitRequest {
	r := &OffsetCommitRequest{
		Version:       1,
		ConsumerGroup: bom.parent.group,
	}
	for s := range bom.subscriptions {
		s.lock.Lock()
		r.AddBlock(s.topic, s.partition, s.offset, 0, s.metadata)
		s.lock.Unlock()
	}
	return r
}

func (bom *brokerOffsetManager) abort(err error) {
	_ = bom.broker.Close() // we don't care about the error this might return, we already have one
	bom.parent.abandonBroker(bom)

	for pom := range bom.subscriptions {
		pom.handleError(err)
		pom.rebalance <- none{}
	}

	for s := range bom.updateSubscriptions {
		if _, ok := bom.subscriptions[s]; !ok {
			s.handleError(err)
			s.rebalance <- none{}
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>fixed logic error. was using break instead of continue in loop<commit_after><|endoftext|>


<commit_before>package api

import (
	"net/url"
	"path/filepath"
	"testing"
	"time"

	"github.com/NebulousLabs/Sia/build"
)

// TestIntegrationHosting tests that the host correctly receives payment for
// hosting files.
func TestIntegrationHosting(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	st, err := createServerTester("TestIntegrationHosting")
	if err != nil {
		t.Fatal(err)
	}
	defer st.server.Close()

	// Announce the host.
	err = st.announceHost()
	if err != nil {
		t.Fatal(err)
	}

	// create a file
	path := filepath.Join(build.SiaTestingDir, "api", "TestIntegrationHosting", "test.dat")
	err = createRandFile(path)
	if err != nil {
		t.Fatal(err)
	}

	// upload to host
	uploadValues := url.Values{}
	uploadValues.Set("source", path)
	uploadValues.Set("duration", "10")
	err = st.stdPostAPI("/renter/upload/test", uploadValues)
	if err != nil {
		t.Fatal(err)
	}
	// only one piece will be uploaded (10% at current redundancy)
	var rf RenterFiles
	for i := 0; i < 100 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress != 10); i++ {
		st.getAPI("/renter/files", &rf)
		time.Sleep(50 * time.Millisecond)
	}
	if len(rf.Files) != 1 || rf.Files[0].UploadProgress != 10 {
		t.Fatal("the uploading is not succeeding for some reason")
	}

	// Mine blocks until the host recognizes profit. The host will wait for 12
	// blocks after the storage window has closed to report the profit, a total
	// of 40 blocks should be mined.
	for i := 0; i < 40; i++ {
		st.miner.AddBlock()
	}

	// check profit
	var hg HostGET
	err = st.getAPI("/host", &hg)
	if err != nil {
		t.Fatal(err)
	}
	expRevenue := "15307662222190387473"
	if hg.Revenue.String() != expRevenue {
		t.Fatalf("host's profit was not affected: expected %v, got %v", expRevenue, hg.Revenue)
	}
}

// TestIntegrationRenewing tests that the renter and host manage contract
// renewals properly.
func TestIntegrationRenewing(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	st, err := createServerTester("TestIntegrationRenewing")
	if err != nil {
		t.Fatal(err)
	}
	defer st.server.Close()

	// Announce the host.
	err = st.announceHost()
	if err != nil {
		t.Fatal(err)
	}

	// create a file
	path := filepath.Join(build.SiaTestingDir, "api", "TestIntegrationRenewing", "test.dat")
	err = createRandFile(path)
	if err != nil {
		t.Fatal(err)
	}

	// upload to host, specifying that the file should be renewed
	uploadValues := url.Values{}
	uploadValues.Set("source", path)
	uploadValues.Set("renew", "true")
	err = st.stdPostAPI("/renter/upload/test", uploadValues)
	if err != nil {
		t.Fatal(err)
	}
	// only one piece will be uploaded (10% at current redundancy)
	var rf RenterFiles
	for i := 0; i < 50 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress != 10); i++ {
		st.getAPI("/renter/files", &rf)
		time.Sleep(50 * time.Millisecond)
	}
	if len(rf.Files) != 1 || rf.Files[0].UploadProgress != 10 {
		t.Error(rf.Files[0].UploadProgress)
		t.Fatal("uploading has failed")
	}

	// default expiration is 20 blocks
	expExpiration := st.cs.Height() + 20
	if rf.Files[0].Expiration != expExpiration {
		t.Fatalf("expected expiration of %v, got %v", expExpiration, rf.Files[0].Expiration)
	}

	// mine blocks until we hit the renew threshold (default 20 blocks)
	for st.cs.Height() < expExpiration-20 {
		st.miner.AddBlock()
	}

	// renter should now renew the contract for another 60 blocks
	newExpiration := st.cs.Height() + 60
	for rf.Files[0].Expiration != newExpiration {
		time.Sleep(1 * time.Second)
		st.getAPI("/renter/files", &rf)
	}
}
<commit_msg>restore api integration tests<commit_after>package api

import (
	"net/url"
	"path/filepath"
	"testing"
	"time"

	"github.com/NebulousLabs/Sia/build"
)

// TestIntegrationHosting tests that the host correctly receives payment for
// hosting files.
func TestIntegrationHosting(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	st, err := createServerTester("TestIntegrationHosting")
	if err != nil {
		t.Fatal(err)
	}
	defer st.server.Close()

	// Announce the host.
	err = st.announceHost()
	if err != nil {
		t.Fatal(err)
	}

	// create a file
	path := filepath.Join(build.SiaTestingDir, "api", "TestIntegrationHosting", "test.dat")
	err = createRandFile(path)
	if err != nil {
		t.Fatal(err)
	}

	// upload to host
	uploadValues := url.Values{}
	uploadValues.Set("source", path)
	uploadValues.Set("duration", "10")
	err = st.stdPostAPI("/renter/upload/test", uploadValues)
	if err != nil {
		t.Fatal(err)
	}
	// only one piece will be uploaded (10% at current redundancy)
	var rf RenterFiles
	for i := 0; i < 150 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress != 10); i++ {
		st.getAPI("/renter/files", &rf)
		time.Sleep(50 * time.Millisecond)
	}
	if len(rf.Files) != 1 || rf.Files[0].UploadProgress != 10 {
		t.Fatal("the uploading is not succeeding for some reason")
	}

	// Mine blocks until the host recognizes profit. The host will wait for 12
	// blocks after the storage window has closed to report the profit, a total
	// of 40 blocks should be mined.
	for i := 0; i < 40; i++ {
		st.miner.AddBlock()
	}

	// check profit
	var hg HostGET
	err = st.getAPI("/host", &hg)
	if err != nil {
		t.Fatal(err)
	}
	expRevenue := "15307662222190387473"
	if hg.Revenue.String() != expRevenue {
		t.Fatalf("host's profit was not affected: expected %v, got %v", expRevenue, hg.Revenue)
	}
}

// TestIntegrationRenewing tests that the renter and host manage contract
// renewals properly.
func TestIntegrationRenewing(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	st, err := createServerTester("TestIntegrationRenewing")
	if err != nil {
		t.Fatal(err)
	}
	defer st.server.Close()

	// Announce the host.
	err = st.announceHost()
	if err != nil {
		t.Fatal(err)
	}

	// create a file
	path := filepath.Join(build.SiaTestingDir, "api", "TestIntegrationRenewing", "test.dat")
	err = createRandFile(path)
	if err != nil {
		t.Fatal(err)
	}

	// upload to host, specifying that the file should be renewed
	uploadValues := url.Values{}
	uploadValues.Set("source", path)
	uploadValues.Set("renew", "true")
	err = st.stdPostAPI("/renter/upload/test", uploadValues)
	if err != nil {
		t.Fatal(err)
	}
	// only one piece will be uploaded (10% at current redundancy)
	var rf RenterFiles
	for i := 0; i < 150 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress != 10); i++ {
		st.getAPI("/renter/files", &rf)
		time.Sleep(50 * time.Millisecond)
	}
	if len(rf.Files) != 1 || rf.Files[0].UploadProgress != 10 {
		t.Error(rf.Files[0].UploadProgress)
		t.Fatal("uploading has failed")
	}

	// default expiration is 20 blocks
	expExpiration := st.cs.Height() + 20
	if rf.Files[0].Expiration != expExpiration {
		t.Fatalf("expected expiration of %v, got %v", expExpiration, rf.Files[0].Expiration)
	}

	// mine blocks until we hit the renew threshold (default 10 blocks)
	for st.cs.Height() < expExpiration-10 {
		st.miner.AddBlock()
	}

	// renter should now renew the contract for another 20 blocks
	newExpiration := st.cs.Height() + 20
	for i := 0; i < 5 && rf.Files[0].Expiration != newExpiration; i++ {
		time.Sleep(1 * time.Second)
		st.getAPI("/renter/files", &rf)
	}
}
<|endoftext|>


<commit_before><commit_msg>core: Correctly ensure that State() is a copy<commit_after><|endoftext|>


<commit_before>package functional

import (
	"log"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"github.com/coreos/fleet/functional/platform"
)

var fleetctlBinPath string

func init() {
	fleetctlBinPath = os.Getenv("FLEETCTL_BIN")
	if fleetctlBinPath == "" {
		fmt.Println("FLEETCTL_BIN environment variable must be set")
		os.Exit(1)
	} else if _, err := os.Stat(fleetctlBinPath); err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(1)
	}

	if os.Getenv("SSH_AUTH_SOCK") == "" {
		fmt.Println("SSH_AUTH_SOCK environment variable must be set")
		os.Exit(1)
	}
}

func fleetctl(args ...string) (string, string, error) {
	log.Printf("%s %s", fleetctlBinPath, strings.Join(args, " "))
	var stdoutBytes, stderrBytes bytes.Buffer
	cmd := exec.Command(fleetctlBinPath, args...)
	cmd.Stdout = &stdoutBytes
	cmd.Stderr = &stderrBytes
	err := cmd.Run()
	return stdoutBytes.String(), stderrBytes.String(), err
}

func TestMachineList(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatalf(err.Error())
	}
	defer cluster.DestroyAll()

	if err := cluster.Create(3); err != nil {
		t.Fatalf(err.Error())
	}

	stdout, _, err := fleetctl("list-machines", "--no-legend")
	if err != nil {
		t.Fatalf("Failed to run list-machines: %v", err)
	}

	stdout = strings.TrimSpace(stdout)
	machines := strings.Split(stdout, "\n")
	if len(machines) != 3 {
		t.Errorf("Did not find three machines running: \n%s", stdout)
	}
}

func TestMachineSSH(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatalf(err.Error())
	}
	defer cluster.DestroyAll()

	if err := cluster.Create(3); err != nil {
		t.Fatalf(err.Error())
	}

	stdout, _, err := fleetctl("list-machines", "--no-legend", "-l")
	if err != nil {
		t.Fatalf("Failed to run list-machines: %v", err)
	}

	bootID := strings.SplitN(stdout, "\t", 2)[0]
	stdout, _, err = fleetctl("ssh", bootID, "uptime")
	if err != nil {
		t.Fatalf("Unable to SSH into fleet machine: %v", err)
	}
}

func TestClusterGrowth(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatalf(err.Error())
	}
	defer cluster.DestroyAll()

	if err := cluster.Create(3); err != nil {
		t.Fatalf(err.Error())
	}

	for i := 0; i < 5; i++ {
		unitName := fmt.Sprintf("fixtures/units/conflict.%d.service", i)
		_, _, err := fleetctl("start", unitName)
		if err != nil {
			t.Fatalf("Failed to start unit %s: %v", unitName, err)
		}
	}

	stdout, _, err := fleetctl("list-units", "--no-legend")
	if err != nil {
		t.Fatalf("Failed to run list-units: %v", err)
	}

	units := strings.Split(strings.TrimSpace(stdout), "\n")
	if len(units) != 5 {
		t.Fatalf("Did not find five units in cluster: \n%s", stdout)
	}

	for i := 0; i < 5; i++ {
		stdout, _, err := fleetctl("list-units", "--no-legend")
		if err != nil {
			t.Fatalf("Failed to run list-units: %v", err)
		}

		units := strings.Split(strings.TrimSpace(stdout), "\n")
		if len(units) != 5 {
			t.Fatalf("Did not find five units in cluster: \n%s", stdout)
		}

		states := parseUnitStates(units)
		if activeCount(states) != 3 {
			if i == 4 {
				t.Fatalf("Three units did not become active in time")
			}

			time.Sleep(time.Second)
			continue
		}

		break
	}

	if err := cluster.Create(2); err != nil {
		t.Fatalf(err.Error())
	}

	stdout, _, err = fleetctl("list-machines", "--no-legend")
	if err != nil {
		t.Fatalf("Failed to run list-machines: %v", err)
	}

	machines := strings.Split(strings.TrimSpace(stdout), "\n")
	if len(machines) != 5 {
		t.Fatalf("Did not find five machines running: \n%s", stdout)
	}

	for i := 0; i < 5; i++ {
		stdout, _, err := fleetctl("list-units", "--no-legend")
		if err != nil {
			t.Fatalf("Failed to run list-units: %v", err)
		}

		units := strings.Split(strings.TrimSpace(stdout), "\n")
		if len(units) != 5 {
			t.Fatalf("Did not find five units in cluster: \n%s", stdout)
		}

		states := parseUnitStates(units)
		if activeCount(states) != 5 {
			if i == 4 {
				t.Fatalf("Five units did not become active in time")
			}

			time.Sleep(time.Second)
			continue
		}

		break
	}
}

func parseUnitStates(units []string) []string {
	states := make([]string, len(units))
	for i, unit := range units {
		cols := strings.SplitN(unit, "\t", 6)
		if len(cols) == 6 {
			states[i] = cols[2]
		}
	}
	return states
}

func activeCount(states []string) (count int) {
	for _, state := range states {
		if state == "active" {
			count++
		}
	}
	return
}
<commit_msg>test(functional): Robustify functional testing<commit_after>package functional

import (
	"log"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"github.com/coreos/fleet/functional/platform"
)

var fleetctlBinPath string

func init() {
	fleetctlBinPath = os.Getenv("FLEETCTL_BIN")
	if fleetctlBinPath == "" {
		fmt.Println("FLEETCTL_BIN environment variable must be set")
		os.Exit(1)
	} else if _, err := os.Stat(fleetctlBinPath); err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(1)
	}

	if os.Getenv("SSH_AUTH_SOCK") == "" {
		fmt.Println("SSH_AUTH_SOCK environment variable must be set")
		os.Exit(1)
	}
}

func fleetctl(args ...string) (string, string, error) {
	log.Printf("%s %s", fleetctlBinPath, strings.Join(args, " "))
	var stdoutBytes, stderrBytes bytes.Buffer
	cmd := exec.Command(fleetctlBinPath, args...)
	cmd.Stdout = &stdoutBytes
	cmd.Stderr = &stderrBytes
	err := cmd.Run()
	return stdoutBytes.String(), stderrBytes.String(), err
}

func TestCluster(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatalf(err.Error())
	}
	defer cluster.DestroyAll()

	if err := cluster.Create(3); err != nil {
		t.Fatalf(err.Error())
	}

	var machines []string
	for i := 0; i <= 7; i++ {
		if i == 7 {
			t.Fatalf("Failed to find all three machines within the time limit")
		}

		log.Printf("Waiting 5s for fleet services to check in...")
		time.Sleep(5*time.Second)

		stdout, _, err := fleetctl("list-machines", "--no-legend", "-l")
		if err != nil {
			t.Fatalf("Failed to run list-machines: %v", err)
		}

		stdout = strings.TrimSpace(stdout)
		machines = strings.Split(stdout, "\n")
		if len(machines) == 3 {
			for k, v := range machines {
				machines[k] = strings.SplitN(v, "\t", 2)[0]
			}
			break
		}
	}

	for _, machine := range machines {
		if _, _, err := fleetctl("ssh", "--strict-host-key-checking=false", machine, "uptime"); err != nil {
			t.Errorf("Unable to SSH into fleet machine: %v", err)
		}
	}

	for i := 0; i < 5; i++ {
		unitName := fmt.Sprintf("fixtures/units/conflict.%d.service", i)
		_, _, err := fleetctl("start", "--no-block",  unitName)
		if err != nil {
			t.Fatalf("Failed to start unit %s: %v", unitName, err)
		}
	}

	stdout, _, err := fleetctl("list-units", "--no-legend")
	if err != nil {
		t.Fatalf("Failed to run list-units: %v", err)
	}

	units := strings.Split(strings.TrimSpace(stdout), "\n")
	if len(units) != 5 {
		t.Fatalf("Did not find five units in cluster: \n%s", stdout)
	}

	for i := 0; i < 5; i++ {
		stdout, _, err := fleetctl("list-units", "--no-legend")
		if err != nil {
			t.Fatalf("Failed to run list-units: %v", err)
		}

		units := strings.Split(strings.TrimSpace(stdout), "\n")
		if len(units) != 5 {
			t.Fatalf("Did not find five units in cluster: \n%s", stdout)
		}

		states := parseUnitStates(units)
		if activeCount(states) != 3 {
			if i == 4 {
				t.Fatalf("Three units did not become active in time")
			}

			time.Sleep(time.Second)
			continue
		}

		break
	}

	if err := cluster.Create(2); err != nil {
		t.Fatalf(err.Error())
	}

	for i := 0; i <= 7; i++ {
		if i == 7 {
			t.Fatalf("Failed to find all three machines within the time limit")
		}

		log.Printf("Waiting 5s for fleet services to check in...")
		time.Sleep(5*time.Second)

		stdout, _, err := fleetctl("list-machines", "--no-legend", "-l")
		if err != nil {
			t.Fatalf("Failed to run list-machines: %v", err)
		}

		stdout = strings.TrimSpace(stdout)
		machines = strings.Split(stdout, "\n")
		if len(machines) == 5 {
			for k, v := range machines {
				machines[k] = strings.SplitN(v, "\t", 2)[0]
			}
			break
		}
	}

	for i := 0; i < 5; i++ {
		stdout, _, err := fleetctl("list-units", "--no-legend")
		if err != nil {
			t.Fatalf("Failed to run list-units: %v", err)
		}

		units := strings.Split(strings.TrimSpace(stdout), "\n")
		if len(units) != 5 {
			t.Fatalf("Did not find five units in cluster: \n%s", stdout)
		}

		states := parseUnitStates(units)
		if activeCount(states) != 5 {
			if i == 4 {
				t.Fatalf("Five units did not become active in time")
			}

			time.Sleep(time.Second)
			continue
		}

		break
	}
}

func parseUnitStates(units []string) []string {
	states := make([]string, len(units))
	for i, unit := range units {
		cols := strings.SplitN(unit, "\t", 6)
		if len(cols) == 6 {
			states[i] = cols[2]
		}
	}
	return states
}

func activeCount(states []string) (count int) {
	for _, state := range states {
		if state == "active" {
			count++
		}
	}
	return
}
<|endoftext|>


<commit_before><commit_msg>fix (i think): panic: sync: WaitGroup is reused before previous Wait has returned<commit_after><|endoftext|>


<commit_before>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package resid

import (
	"strings"
)

// Gvk identifies a Kubernetes API type.
// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type Gvk struct {
	Group   string `json:"group,omitempty" yaml:"group,omitempty"`
	Version string `json:"version,omitempty" yaml:"version,omitempty"`
	Kind    string `json:"kind,omitempty" yaml:"kind,omitempty"`
}

// FromKind makes a Gvk with only the kind specified.
func FromKind(k string) Gvk {
	return Gvk{
		Kind: k,
	}
}

// GvkFromString makes a Gvk with a string,
// which is constructed by String() function
func GvkFromString(s string) Gvk {
	values := strings.Split(s, fieldSep)
	g := values[0]
	if g == noGroup {
		g = ""
	}
	v := values[1]
	if v == noVersion {
		v = ""
	}
	k := values[2]
	if k == noKind {
		k = ""
	}
	return Gvk{
		Group:   g,
		Version: v,
		Kind:    k,
	}
}

// Values that are brief but meaningful in logs.
const (
	noGroup   = "~G"
	noVersion = "~V"
	noKind    = "~K"
	fieldSep  = "_"
)

// String returns a string representation of the GVK.
func (x Gvk) String() string {
	g := x.Group
	if g == "" {
		g = noGroup
	}
	v := x.Version
	if v == "" {
		v = noVersion
	}
	k := x.Kind
	if k == "" {
		k = noKind
	}
	return strings.Join([]string{g, v, k}, fieldSep)
}

// Equals returns true if the Gvk's have equal fields.
func (x Gvk) Equals(o Gvk) bool {
	return x.Group == o.Group && x.Version == o.Version && x.Kind == o.Kind
}

// An attempt to order things to help k8s, e.g.
// a Service should come before things that refer to it.
// Namespace should be first.
// In some cases order just specified to provide determinism.
var orderFirst = []string{
	"Namespace",
	"ResourceQuota",
	"StorageClass",
	"CustomResourceDefinition",
	"ServiceAccount",
	"PodSecurityPolicy",
	"Role",
	"ClusterRole",
	"RoleBinding",
	"ClusterRoleBinding",
	"ConfigMap",
	"Secret",
	"Service",
	"LimitRange",
	"PriorityClass",
	"Deployment",
	"StatefulSet",
	"CronJob",
	"PodDisruptionBudget",
}
var orderLast = []string{
	"MutatingWebhookConfiguration",
	"ValidatingWebhookConfiguration",
}
var typeOrders = func() map[string]int {
	m := map[string]int{}
	for i, n := range orderFirst {
		m[n] = -len(orderFirst) + i
	}
	for i, n := range orderLast {
		m[n] = 1 + i
	}
	return m
}()

// IsLessThan returns true if self is less than the argument.
func (x Gvk) IsLessThan(o Gvk) bool {
	indexI := typeOrders[x.Kind]
	indexJ := typeOrders[o.Kind]
	if indexI != indexJ {
		return indexI < indexJ
	}
	return x.String() < o.String()
}

// IsSelected returns true if `selector` selects `x`; otherwise, false.
// If `selector` and `x` are the same, return true.
// If `selector` is nil, it is considered a wildcard match, returning true.
// If selector fields are empty, they are considered wildcards matching
// anything in the corresponding fields, e.g.
//
// this item:
//       <Group: "extensions", Version: "v1beta1", Kind: "Deployment">
//
// is selected by
//       <Group: "",           Version: "",        Kind: "Deployment">
//
// but rejected by
//       <Group: "apps",       Version: "",        Kind: "Deployment">
//
func (x Gvk) IsSelected(selector *Gvk) bool {
	if selector == nil {
		return true
	}
	if len(selector.Group) > 0 {
		if x.Group != selector.Group {
			return false
		}
	}
	if len(selector.Version) > 0 {
		if x.Version != selector.Version {
			return false
		}
	}
	if len(selector.Kind) > 0 {
		if x.Kind != selector.Kind {
			return false
		}
	}
	return true
}

var notNamespaceableKinds = []string{
	"APIService",
	"CSIDriver",
	"CSINode",
	"CertificateSigningRequest",
	"Cluster",
	"ClusterRole",
	"ClusterRoleBinding",
	"ComponentStatus",
	"CustomResourceDefinition",
	"MutatingWebhookConfiguration",
	"Namespace",
	"Node",
	"PersistentVolume",
	"PodSecurityPolicy",
	"PriorityClass",
	"RuntimeClass",
	"SelfSubjectAccessReview",
	"SelfSubjectRulesReview",
	"StorageClass",
	"SubjectAccessReview",
	"TokenReview",
	"ValidatingWebhookConfiguration",
	"VolumeAttachment",
}

// IsNamespaceableKind returns true if x is a namespaceable Gvk
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
func (x Gvk) IsNamespaceableKind() bool {
	for _, k := range notNamespaceableKinds {
		if k == x.Kind {
			return false
		}
	}
	return true
}
<commit_msg>Order PersistentVolume before Deployment<commit_after>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package resid

import (
	"strings"
)

// Gvk identifies a Kubernetes API type.
// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type Gvk struct {
	Group   string `json:"group,omitempty" yaml:"group,omitempty"`
	Version string `json:"version,omitempty" yaml:"version,omitempty"`
	Kind    string `json:"kind,omitempty" yaml:"kind,omitempty"`
}

// FromKind makes a Gvk with only the kind specified.
func FromKind(k string) Gvk {
	return Gvk{
		Kind: k,
	}
}

// GvkFromString makes a Gvk with a string,
// which is constructed by String() function
func GvkFromString(s string) Gvk {
	values := strings.Split(s, fieldSep)
	g := values[0]
	if g == noGroup {
		g = ""
	}
	v := values[1]
	if v == noVersion {
		v = ""
	}
	k := values[2]
	if k == noKind {
		k = ""
	}
	return Gvk{
		Group:   g,
		Version: v,
		Kind:    k,
	}
}

// Values that are brief but meaningful in logs.
const (
	noGroup   = "~G"
	noVersion = "~V"
	noKind    = "~K"
	fieldSep  = "_"
)

// String returns a string representation of the GVK.
func (x Gvk) String() string {
	g := x.Group
	if g == "" {
		g = noGroup
	}
	v := x.Version
	if v == "" {
		v = noVersion
	}
	k := x.Kind
	if k == "" {
		k = noKind
	}
	return strings.Join([]string{g, v, k}, fieldSep)
}

// Equals returns true if the Gvk's have equal fields.
func (x Gvk) Equals(o Gvk) bool {
	return x.Group == o.Group && x.Version == o.Version && x.Kind == o.Kind
}

// An attempt to order things to help k8s, e.g.
// a Service should come before things that refer to it.
// Namespace should be first.
// In some cases order just specified to provide determinism.
var orderFirst = []string{
	"Namespace",
	"ResourceQuota",
	"StorageClass",
	"CustomResourceDefinition",
	"ServiceAccount",
	"PodSecurityPolicy",
	"Role",
	"ClusterRole",
	"RoleBinding",
	"ClusterRoleBinding",
	"ConfigMap",
	"Secret",
	"Service",
	"LimitRange",
	"PriorityClass",
	"PersistentVolume",
	"PersistentVolumeClaim",
	"Deployment",
	"StatefulSet",
	"CronJob",
	"PodDisruptionBudget",
}
var orderLast = []string{
	"MutatingWebhookConfiguration",
	"ValidatingWebhookConfiguration",
}
var typeOrders = func() map[string]int {
	m := map[string]int{}
	for i, n := range orderFirst {
		m[n] = -len(orderFirst) + i
	}
	for i, n := range orderLast {
		m[n] = 1 + i
	}
	return m
}()

// IsLessThan returns true if self is less than the argument.
func (x Gvk) IsLessThan(o Gvk) bool {
	indexI := typeOrders[x.Kind]
	indexJ := typeOrders[o.Kind]
	if indexI != indexJ {
		return indexI < indexJ
	}
	return x.String() < o.String()
}

// IsSelected returns true if `selector` selects `x`; otherwise, false.
// If `selector` and `x` are the same, return true.
// If `selector` is nil, it is considered a wildcard match, returning true.
// If selector fields are empty, they are considered wildcards matching
// anything in the corresponding fields, e.g.
//
// this item:
//       <Group: "extensions", Version: "v1beta1", Kind: "Deployment">
//
// is selected by
//       <Group: "",           Version: "",        Kind: "Deployment">
//
// but rejected by
//       <Group: "apps",       Version: "",        Kind: "Deployment">
//
func (x Gvk) IsSelected(selector *Gvk) bool {
	if selector == nil {
		return true
	}
	if len(selector.Group) > 0 {
		if x.Group != selector.Group {
			return false
		}
	}
	if len(selector.Version) > 0 {
		if x.Version != selector.Version {
			return false
		}
	}
	if len(selector.Kind) > 0 {
		if x.Kind != selector.Kind {
			return false
		}
	}
	return true
}

var notNamespaceableKinds = []string{
	"APIService",
	"CSIDriver",
	"CSINode",
	"CertificateSigningRequest",
	"Cluster",
	"ClusterRole",
	"ClusterRoleBinding",
	"ComponentStatus",
	"CustomResourceDefinition",
	"MutatingWebhookConfiguration",
	"Namespace",
	"Node",
	"PersistentVolume",
	"PodSecurityPolicy",
	"PriorityClass",
	"RuntimeClass",
	"SelfSubjectAccessReview",
	"SelfSubjectRulesReview",
	"StorageClass",
	"SubjectAccessReview",
	"TokenReview",
	"ValidatingWebhookConfiguration",
	"VolumeAttachment",
}

// IsNamespaceableKind returns true if x is a namespaceable Gvk
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
func (x Gvk) IsNamespaceableKind() bool {
	for _, k := range notNamespaceableKinds {
		if k == x.Kind {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

package chartutil

import (
	"log"
	"strings"

	"helm.sh/helm/pkg/chart"
)

// ProcessDependencies checks through this chart's dependencies, processing accordingly.
func ProcessDependencies(c *chart.Chart, v Values) error {
	if err := processDependencyEnabled(c, v); err != nil {
		return err
	}
	return processDependencyImportValues(c)
}

// processDependencyConditions disables charts based on condition path value in values
func processDependencyConditions(reqs []*chart.Dependency, cvals Values) {
	if reqs == nil {
		return
	}
	for _, r := range reqs {
		var hasTrue, hasFalse bool
		for _, c := range strings.Split(strings.TrimSpace(r.Condition), ",") {
			if len(c) > 0 {
				// retrieve value
				vv, err := cvals.PathValue(c)
				if err == nil {
					// if not bool, warn
					if bv, ok := vv.(bool); ok {
						if bv {
							hasTrue = true
						} else {
							hasFalse = true
						}
					} else {
						log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name)
					}
				} else if _, ok := err.(ErrNoValue); !ok {
					// this is a real error
					log.Printf("Warning: PathValue returned error %v", err)
				}
				if vv != nil {
					// got first value, break loop
					break
				}
			}
		}
		if !hasTrue && hasFalse {
			r.Enabled = false
		} else if hasTrue {
			r.Enabled = true

		}
	}
}

// processDependencyTags disables charts based on tags in values
func processDependencyTags(reqs []*chart.Dependency, cvals Values) {
	if reqs == nil {
		return
	}
	vt, err := cvals.Table("tags")
	if err != nil {
		return
	}
	for _, r := range reqs {
		var hasTrue, hasFalse bool
		for _, k := range r.Tags {
			if b, ok := vt[k]; ok {
				// if not bool, warn
				if bv, ok := b.(bool); ok {
					if bv {
						hasTrue = true
					} else {
						hasFalse = true
					}
				} else {
					log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name)
				}
			}
		}
		if !hasTrue && hasFalse {
			r.Enabled = false
		} else if hasTrue || !hasTrue && !hasFalse {
			r.Enabled = true
		}
	}
}

func getAliasDependency(charts []*chart.Chart, dep *chart.Dependency) *chart.Chart {
	for _, c := range charts {
		if c == nil {
			continue
		}
		if c.Name() != dep.Name {
			continue
		}
		if !IsCompatibleRange(dep.Version, c.Metadata.Version) {
			continue
		}

		out := *c
		md := *c.Metadata
		out.Metadata = &md

		if dep.Alias != "" {
			md.Name = dep.Alias
		}
		return &out
	}
	return nil
}

// processDependencyEnabled removes disabled charts from dependencies
func processDependencyEnabled(c *chart.Chart, v map[string]interface{}) error {
	if c.Metadata.Dependencies == nil {
		return nil
	}

	var chartDependencies []*chart.Chart
	// If any dependency is not a part of Chart.yaml
	// then this should be added to chartDependencies.
	// However, if the dependency is already specified in Chart.yaml
	// we should not add it, as it would be anyways processed from Chart.yaml

Loop:
	for _, existing := range c.Dependencies() {
		for _, req := range c.Metadata.Dependencies {
			if existing.Name() == req.Name && IsCompatibleRange(req.Version, existing.Metadata.Version) {
				continue Loop
			}
		}
		chartDependencies = append(chartDependencies, existing)
	}

	for _, req := range c.Metadata.Dependencies {
		if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil {
			chartDependencies = append(chartDependencies, chartDependency)
		}
		if req.Alias != "" {
			req.Name = req.Alias
		}
	}
	c.SetDependencies(chartDependencies...)

	// set all to true
	for _, lr := range c.Metadata.Dependencies {
		lr.Enabled = true
	}
	cvals, err := CoalesceValues(c, v)
	if err != nil {
		return err
	}
	// flag dependencies as enabled/disabled
	processDependencyTags(c.Metadata.Dependencies, cvals)
	processDependencyConditions(c.Metadata.Dependencies, cvals)
	// make a map of charts to remove
	rm := map[string]struct{}{}
	for _, r := range c.Metadata.Dependencies {
		if !r.Enabled {
			// remove disabled chart
			rm[r.Name] = struct{}{}
		}
	}
	// don't keep disabled charts in new slice
	cd := []*chart.Chart{}
	copy(cd, c.Dependencies()[:0])
	for _, n := range c.Dependencies() {
		if _, ok := rm[n.Metadata.Name]; !ok {
			cd = append(cd, n)
		}
	}

	// recursively call self to process sub dependencies
	for _, t := range cd {
		if err := processDependencyEnabled(t, cvals); err != nil {
			return err
		}
	}
	c.SetDependencies(cd...)

	return nil
}

// pathToMap creates a nested map given a YAML path in dot notation.
func pathToMap(path string, data map[string]interface{}) map[string]interface{} {
	if path == "." {
		return data
	}
	return set(parsePath(path), data)
}

func set(path []string, data map[string]interface{}) map[string]interface{} {
	if len(path) == 0 {
		return nil
	}
	cur := data
	for i := len(path) - 1; i >= 0; i-- {
		cur = map[string]interface{}{path[i]: cur}
	}
	return cur
}

// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field.
func processImportValues(c *chart.Chart) error {
	if c.Metadata.Dependencies == nil {
		return nil
	}
	// combine chart values and empty config to get Values
	cvals, err := CoalesceValues(c, nil)
	if err != nil {
		return err
	}
	b := make(map[string]interface{})
	// import values from each dependency if specified in import-values
	for _, r := range c.Metadata.Dependencies {
		var outiv []interface{}
		for _, riv := range r.ImportValues {
			switch iv := riv.(type) {
			case map[string]interface{}:
				child := iv["child"].(string)
				parent := iv["parent"].(string)

				outiv = append(outiv, map[string]string{
					"child":  child,
					"parent": parent,
				})

				// get child table
				vv, err := cvals.Table(r.Name + "." + child)
				if err != nil {
					log.Printf("Warning: ImportValues missing table: %v", err)
					continue
				}
				// create value map from child to be merged into parent
				b = CoalesceTables(cvals, pathToMap(parent, vv.AsMap()))
			case string:
				child := "exports." + iv
				outiv = append(outiv, map[string]string{
					"child":  child,
					"parent": ".",
				})
				vm, err := cvals.Table(r.Name + "." + child)
				if err != nil {
					log.Printf("Warning: ImportValues missing table: %v", err)
					continue
				}
				b = CoalesceTables(b, vm.AsMap())
			}
		}
		// set our formatted import values
		r.ImportValues = outiv
	}

	// set the new values
	c.Values = CoalesceTables(b, cvals)

	return nil
}

// processDependencyImportValues imports specified chart values from child to parent.
func processDependencyImportValues(c *chart.Chart) error {
	for _, d := range c.Dependencies() {
		// recurse
		if err := processDependencyImportValues(d); err != nil {
			return err
		}
	}
	return processImportValues(c)
}
<commit_msg>ref(pkg/chartutil): Simplify processDependencyConditions<commit_after>/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

package chartutil

import (
	"log"
	"strings"

	"helm.sh/helm/pkg/chart"
)

// ProcessDependencies checks through this chart's dependencies, processing accordingly.
func ProcessDependencies(c *chart.Chart, v Values) error {
	if err := processDependencyEnabled(c, v); err != nil {
		return err
	}
	return processDependencyImportValues(c)
}

// processDependencyConditions disables charts based on condition path value in values
func processDependencyConditions(reqs []*chart.Dependency, cvals Values) {
	if reqs == nil {
		return
	}
	for _, r := range reqs {
		for _, c := range strings.Split(strings.TrimSpace(r.Condition), ",") {
			if len(c) > 0 {
				// retrieve value
				vv, err := cvals.PathValue(c)
				if err == nil {
					// if not bool, warn
					if bv, ok := vv.(bool); ok {
						r.Enabled = bv
						break
					} else {
						log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name)
					}
				} else if _, ok := err.(ErrNoValue); !ok {
					// this is a real error
					log.Printf("Warning: PathValue returned error %v", err)
				}
			}
		}
	}
}

// processDependencyTags disables charts based on tags in values
func processDependencyTags(reqs []*chart.Dependency, cvals Values) {
	if reqs == nil {
		return
	}
	vt, err := cvals.Table("tags")
	if err != nil {
		return
	}
	for _, r := range reqs {
		var hasTrue, hasFalse bool
		for _, k := range r.Tags {
			if b, ok := vt[k]; ok {
				// if not bool, warn
				if bv, ok := b.(bool); ok {
					if bv {
						hasTrue = true
					} else {
						hasFalse = true
					}
				} else {
					log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name)
				}
			}
		}
		if !hasTrue && hasFalse {
			r.Enabled = false
		} else if hasTrue || !hasTrue && !hasFalse {
			r.Enabled = true
		}
	}
}

func getAliasDependency(charts []*chart.Chart, dep *chart.Dependency) *chart.Chart {
	for _, c := range charts {
		if c == nil {
			continue
		}
		if c.Name() != dep.Name {
			continue
		}
		if !IsCompatibleRange(dep.Version, c.Metadata.Version) {
			continue
		}

		out := *c
		md := *c.Metadata
		out.Metadata = &md

		if dep.Alias != "" {
			md.Name = dep.Alias
		}
		return &out
	}
	return nil
}

// processDependencyEnabled removes disabled charts from dependencies
func processDependencyEnabled(c *chart.Chart, v map[string]interface{}) error {
	if c.Metadata.Dependencies == nil {
		return nil
	}

	var chartDependencies []*chart.Chart
	// If any dependency is not a part of Chart.yaml
	// then this should be added to chartDependencies.
	// However, if the dependency is already specified in Chart.yaml
	// we should not add it, as it would be anyways processed from Chart.yaml

Loop:
	for _, existing := range c.Dependencies() {
		for _, req := range c.Metadata.Dependencies {
			if existing.Name() == req.Name && IsCompatibleRange(req.Version, existing.Metadata.Version) {
				continue Loop
			}
		}
		chartDependencies = append(chartDependencies, existing)
	}

	for _, req := range c.Metadata.Dependencies {
		if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil {
			chartDependencies = append(chartDependencies, chartDependency)
		}
		if req.Alias != "" {
			req.Name = req.Alias
		}
	}
	c.SetDependencies(chartDependencies...)

	// set all to true
	for _, lr := range c.Metadata.Dependencies {
		lr.Enabled = true
	}
	cvals, err := CoalesceValues(c, v)
	if err != nil {
		return err
	}
	// flag dependencies as enabled/disabled
	processDependencyTags(c.Metadata.Dependencies, cvals)
	processDependencyConditions(c.Metadata.Dependencies, cvals)
	// make a map of charts to remove
	rm := map[string]struct{}{}
	for _, r := range c.Metadata.Dependencies {
		if !r.Enabled {
			// remove disabled chart
			rm[r.Name] = struct{}{}
		}
	}
	// don't keep disabled charts in new slice
	cd := []*chart.Chart{}
	copy(cd, c.Dependencies()[:0])
	for _, n := range c.Dependencies() {
		if _, ok := rm[n.Metadata.Name]; !ok {
			cd = append(cd, n)
		}
	}

	// recursively call self to process sub dependencies
	for _, t := range cd {
		if err := processDependencyEnabled(t, cvals); err != nil {
			return err
		}
	}
	c.SetDependencies(cd...)

	return nil
}

// pathToMap creates a nested map given a YAML path in dot notation.
func pathToMap(path string, data map[string]interface{}) map[string]interface{} {
	if path == "." {
		return data
	}
	return set(parsePath(path), data)
}

func set(path []string, data map[string]interface{}) map[string]interface{} {
	if len(path) == 0 {
		return nil
	}
	cur := data
	for i := len(path) - 1; i >= 0; i-- {
		cur = map[string]interface{}{path[i]: cur}
	}
	return cur
}

// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field.
func processImportValues(c *chart.Chart) error {
	if c.Metadata.Dependencies == nil {
		return nil
	}
	// combine chart values and empty config to get Values
	cvals, err := CoalesceValues(c, nil)
	if err != nil {
		return err
	}
	b := make(map[string]interface{})
	// import values from each dependency if specified in import-values
	for _, r := range c.Metadata.Dependencies {
		var outiv []interface{}
		for _, riv := range r.ImportValues {
			switch iv := riv.(type) {
			case map[string]interface{}:
				child := iv["child"].(string)
				parent := iv["parent"].(string)

				outiv = append(outiv, map[string]string{
					"child":  child,
					"parent": parent,
				})

				// get child table
				vv, err := cvals.Table(r.Name + "." + child)
				if err != nil {
					log.Printf("Warning: ImportValues missing table: %v", err)
					continue
				}
				// create value map from child to be merged into parent
				b = CoalesceTables(cvals, pathToMap(parent, vv.AsMap()))
			case string:
				child := "exports." + iv
				outiv = append(outiv, map[string]string{
					"child":  child,
					"parent": ".",
				})
				vm, err := cvals.Table(r.Name + "." + child)
				if err != nil {
					log.Printf("Warning: ImportValues missing table: %v", err)
					continue
				}
				b = CoalesceTables(b, vm.AsMap())
			}
		}
		// set our formatted import values
		r.ImportValues = outiv
	}

	// set the new values
	c.Values = CoalesceTables(b, cvals)

	return nil
}

// processDependencyImportValues imports specified chart values from child to parent.
func processDependencyImportValues(c *chart.Chart) error {
	for _, d := range c.Dependencies() {
		// recurse
		if err := processDependencyImportValues(d); err != nil {
			return err
		}
	}
	return processImportValues(c)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package features

import (
	"k8s.io/component-base/featuregate"
)

const (
	// Every feature gate should add method here following this template:
	//
	// // owner: @username
	// // alpha: v1.4
	// MyFeature() bool

	// owner: @khenidak
	// alpha: v1.15
	//
	// Enables ipv6 dual stack
	// Original copy from k8s.io/kubernetes/pkg/features/kube_features.go
	IPv6DualStack featuregate.Feature = "IPv6DualStack"

	// owner: @jiahuif
	// alpha: v1.21
	// beta:  v1.22
	// GA:    v1.24
	//
	// Enables Leader Migration for kube-controller-manager and cloud-controller-manager
	// copied and sync'ed from k8s.io/kubernetes/pkg/features/kube_features.go
	ControllerManagerLeaderMigration featuregate.Feature = "ControllerManagerLeaderMigration"
)

func SetupCurrentKubernetesSpecificFeatureGates(featuregates featuregate.MutableFeatureGate) error {
	return featuregates.Add(cloudPublicFeatureGates)
}

// cloudPublicFeatureGates consists of cloud-specific feature keys.
// To add a new feature, define a key for it at k8s.io/api/pkg/features and add it here.
var cloudPublicFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
	IPv6DualStack:                    {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
	ControllerManagerLeaderMigration: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.26
}
<commit_msg>features: reduce risk of merge conflicts<commit_after>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package features

import (
	"k8s.io/component-base/featuregate"
)

const (
	// Every feature gate should add method here following this template:
	//
	// // owner: @username
	// // alpha: v1.4
	// MyFeature featuregate.Feature = "MyFeature"
	//
	// Feature gates should be listed in alphabetical, case-sensitive
	// (upper before any lower case character) order. This reduces the risk
	// of code conflicts because changes are more likely to be scattered
	// across the file.

	// owner: @jiahuif
	// alpha: v1.21
	// beta:  v1.22
	// GA:    v1.24
	//
	// Enables Leader Migration for kube-controller-manager and cloud-controller-manager
	// copied and sync'ed from k8s.io/kubernetes/pkg/features/kube_features.go
	ControllerManagerLeaderMigration featuregate.Feature = "ControllerManagerLeaderMigration"

	// owner: @khenidak
	// alpha: v1.15
	//
	// Enables ipv6 dual stack
	// Original copy from k8s.io/kubernetes/pkg/features/kube_features.go
	IPv6DualStack featuregate.Feature = "IPv6DualStack"
)

func SetupCurrentKubernetesSpecificFeatureGates(featuregates featuregate.MutableFeatureGate) error {
	return featuregates.Add(cloudPublicFeatureGates)
}

// cloudPublicFeatureGates consists of cloud-specific feature keys.
// To add a new feature, define a key for it at k8s.io/api/pkg/features and add it here.
var cloudPublicFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
	ControllerManagerLeaderMigration: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.26

	IPv6DualStack: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
}
<|endoftext|>