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

package regtest

import (
	"testing"

	"golang.org/x/tools/internal/lsp"
	"golang.org/x/tools/internal/lsp/fake"
	"golang.org/x/tools/internal/lsp/protocol"
)

// Use mod.com for all go.mod files due to golang/go#35230.
const exampleProgram = `
-- go.mod --
module mod.com

go 1.12
-- main.go --
package main

import "fmt"

func main() {
	fmt.Println("Hello World.")
}`

func TestDiagnosticErrorInEditedFile(t *testing.T) {
	// This test is very basic: start with a clean Go program, make an error, and
	// get a diagnostic for that error. However, it also demonstrates how to
	// combine Expectations to await more complex state in the editor.
	runner.Run(t, exampleProgram, func(t *testing.T, env *Env) {
		// Deleting the 'n' at the end of Println should generate a single error
		// diagnostic.
		env.OpenFile("main.go")
		env.RegexpReplace("main.go", "Printl(n)", "")
		env.Await(
			// Once we have gotten diagnostics for the change above, we should
			// satisfy the DiagnosticAtRegexp assertion.
			OnceMet(
				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
				env.DiagnosticAtRegexp("main.go", "Printl"),
			),
			// Assert that this test has sent no error logs to the client. This is not
			// strictly necessary for testing this regression, but is included here
			// as an example of using the NoErrorLogs() expectation. Feel free to
			// delete.
			NoErrorLogs(),
		)
	})
}

const onlyMod = `
-- go.mod --
module mod.com

go 1.12
`

func TestMissingImportDiagsClearOnFirstFile(t *testing.T) {
	t.Parallel()
	runner.Run(t, onlyMod, func(t *testing.T, env *Env) {
		env.CreateBuffer("main.go", `package main

func m() {
	log.Println()
}
`)
		env.Await(
			env.DiagnosticAtRegexp("main.go", "log"),
		)
		env.SaveBuffer("main.go")
		env.Await(
			EmptyDiagnostics("main.go"),
		)
	})
}

const brokenFile = `package main

const Foo = "abc
`

func TestDiagnosticErrorInNewFile(t *testing.T) {
	runner.Run(t, brokenFile, func(t *testing.T, env *Env) {
		env.CreateBuffer("broken.go", brokenFile)
		env.Await(env.DiagnosticAtRegexp("broken.go", "\"abc"))
	})
}

// badPackage contains a duplicate definition of the 'a' const.
const badPackage = `
-- go.mod --
module mod.com

go 1.12
-- a.go --
package consts

const a = 1
-- b.go --
package consts

const a = 2
`

func TestDiagnosticClearingOnEdit(t *testing.T) {
	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
		env.OpenFile("b.go")
		env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))

		// Fix the error by editing the const name in b.go to `b`.
		env.RegexpReplace("b.go", "(a) = 2", "b")
		env.Await(
			EmptyDiagnostics("a.go"),
			EmptyDiagnostics("b.go"),
		)
	})
}

func TestDiagnosticClearingOnDelete_Issue37049(t *testing.T) {
	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
		env.OpenFile("a.go")
		env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
		env.RemoveFileFromWorkspace("b.go")

		env.Await(EmptyDiagnostics("a.go"), EmptyDiagnostics("b.go"))
	})
}

func TestDiagnosticClearingOnClose(t *testing.T) {
	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
		env.CreateBuffer("c.go", `package consts

const a = 3`)
		env.Await(
			env.DiagnosticAtRegexp("a.go", "a = 1"),
			env.DiagnosticAtRegexp("b.go", "a = 2"),
			env.DiagnosticAtRegexp("c.go", "a = 3"))
		env.CloseBuffer("c.go")
		env.Await(
			env.DiagnosticAtRegexp("a.go", "a = 1"),
			env.DiagnosticAtRegexp("b.go", "a = 2"),
			EmptyDiagnostics("c.go"))
	})
}

func TestIssue37978(t *testing.T) {
	runner.Run(t, exampleProgram, func(t *testing.T, env *Env) {
		// Create a new workspace-level directory and empty file.
		env.CreateBuffer("c/c.go", "")

		// Write the file contents with a missing import.
		env.EditBuffer("c/c.go", fake.Edit{
			Text: `package c

const a = http.MethodGet
`,
		})
		env.Await(
			env.DiagnosticAtRegexp("c/c.go", "http.MethodGet"),
		)
		// Save file, which will organize imports, adding the expected import.
		// Expect the diagnostics to clear.
		env.SaveBuffer("c/c.go")
		env.Await(
			EmptyDiagnostics("c/c.go"),
		)
	})
}

const noMod = `
-- main.go --
package main

import "mod.com/bob"

func main() {
	bob.Hello()
}
-- bob/bob.go --
package bob

func Hello() {
	var x int
}
`

// TestNoMod confirms that gopls continues to work when a user adds a go.mod
// file to their workspace.
func TestNoMod(t *testing.T) {
	t.Run("manual", func(t *testing.T) {
		runner.Run(t, noMod, func(t *testing.T, env *Env) {
			env.Await(
				env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
			)
			env.CreateBuffer("go.mod", `module mod.com

	go 1.12
`)
			env.SaveBuffer("go.mod")
			env.Await(
				EmptyDiagnostics("main.go"),
				env.DiagnosticAtRegexp("bob/bob.go", "x"),
			)
		})
	})
	t.Run("initialized", func(t *testing.T) {
		runner.Run(t, noMod, func(t *testing.T, env *Env) {
			env.Await(
				env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
			)
			if err := env.W.RunGoCommand(env.Ctx, "mod", "init", "mod.com"); err != nil {
				t.Fatal(err)
			}
			env.Await(
				EmptyDiagnostics("main.go"),
				env.DiagnosticAtRegexp("bob/bob.go", "x"),
			)
		})
	})
}

const testPackage = `
-- go.mod --
module mod.com

go 1.12
-- lib.go --
package lib

func Hello(x string) {
	_ = x
}
-- lib_test.go --
package lib

import "testing"

type testStruct struct{
	name string
}

func TestHello(t *testing.T) {
	testStructs := []*testStruct{
		&testStruct{"hello"},
		&testStruct{"goodbye"},
	}
	for y := range testStructs {
		_ = y
	}
}
`

func TestIssue38267(t *testing.T) {
	runner.Run(t, testPackage, func(t *testing.T, env *Env) {
		env.OpenFile("lib_test.go")
		env.Await(
			DiagnosticAt("lib_test.go", 10, 2),
			DiagnosticAt("lib_test.go", 11, 2),
		)
		env.OpenFile("lib.go")
		env.RegexpReplace("lib.go", "_ = x", "var y int")
		env.Await(
			env.DiagnosticAtRegexp("lib.go", "y int"),
			EmptyDiagnostics("lib_test.go"),
		)
	})
}

const packageChange = `
-- go.mod --
module fake
-- a.go --
package foo
func main() {}
`

func TestPackageChange_Issue38328(t *testing.T) {
	runner.Run(t, packageChange, func(t *testing.T, env *Env) {
		env.OpenFile("a.go")
		env.RegexpReplace("a.go", "foo", "foox")
		env.Await(
			// When the bug reported in #38328 was present, we didn't get erroneous
			// file diagnostics until after the didChange message generated by the
			// package renaming was fully processed. Therefore, in order for this
			// test to actually exercise the bug, we must wait until that work has
			// completed.
			EmptyDiagnostics("a.go"),
			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
		)
	})
}

const testPackageWithRequire = `
-- go.mod --
module mod.com

go 1.12

require (
	foo.test v1.2.3
)
-- print.go --
package lib

import (
	"fmt"

	"foo.test/bar"
)

func PrintAnswer() {
	fmt.Printf("answer: %s", bar.Answer)
}
`

const testPackageWithRequireProxy = `
-- foo.test@v1.2.3/go.mod --
module foo.test

go 1.12
-- foo.test@v1.2.3/bar/const.go --
package bar

const Answer = 42
`

func TestResolveDiagnosticWithDownload(t *testing.T) {
	runner.Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
		env.OpenFile("print.go")
		// Check that gopackages correctly loaded this dependency. We should get a
		// diagnostic for the wrong formatting type.
		// TODO: we should be able to easily also match the diagnostic message.
		env.Await(env.DiagnosticAtRegexp("print.go", "fmt.Printf"))
	}, WithProxy(testPackageWithRequireProxy))
}

func TestMissingDependency(t *testing.T) {
	runner.Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
		env.OpenFile("print.go")
		env.Await(LogMatching(protocol.Error, "initial workspace load failed"))
	})
}

func TestAdHocPackages_Issue36951(t *testing.T) {
	const adHoc = `
-- b/b.go --
package b

func Hello() {
	var x int
}
`
	runner.Run(t, adHoc, func(t *testing.T, env *Env) {
		env.OpenFile("b/b.go")
		env.Await(env.DiagnosticAtRegexp("b/b.go", "x"))
	})
}

func TestNoGOPATH_Issue37984(t *testing.T) {
	const missingImport = `
-- main.go --
package main

func _() {
	fmt.Println("Hello World")
}
`
	runner.Run(t, missingImport, func(t *testing.T, env *Env) {
		env.OpenFile("main.go")
		env.Await(env.DiagnosticAtRegexp("main.go", "fmt"))
		if err := env.E.OrganizeImports(env.Ctx, "main.go"); err == nil {
			t.Fatalf("organize imports should fail with an empty GOPATH")
		}
	}, WithEnv("GOPATH="))
}

func TestEqualInEnv_Issue38669(t *testing.T) {
	const missingImport = `
-- go.mod --
module mod.com

-- main.go --
package main

var _ = x.X
-- x/x.go --
package x

var X = 0
`
	runner.Run(t, missingImport, func(t *testing.T, env *Env) {
		env.OpenFile("main.go")
		env.OrganizeImports("main.go")
		env.Await(EmptyDiagnostics("main.go"))
	}, WithEnv("GOFLAGS=-tags=foo"))
}
<commit_msg>internal/lsp/regtest: cosmetic fixes<commit_after>// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package regtest

import (
	"testing"

	"golang.org/x/tools/internal/lsp"
	"golang.org/x/tools/internal/lsp/fake"
	"golang.org/x/tools/internal/lsp/protocol"
)

// Use mod.com for all go.mod files due to golang/go#35230.
const exampleProgram = `
-- go.mod --
module mod.com

go 1.12
-- main.go --
package main

import "fmt"

func main() {
	fmt.Println("Hello World.")
}`

func TestDiagnosticErrorInEditedFile(t *testing.T) {
	// This test is very basic: start with a clean Go program, make an error, and
	// get a diagnostic for that error. However, it also demonstrates how to
	// combine Expectations to await more complex state in the editor.
	runner.Run(t, exampleProgram, func(t *testing.T, env *Env) {
		// Deleting the 'n' at the end of Println should generate a single error
		// diagnostic.
		env.OpenFile("main.go")
		env.RegexpReplace("main.go", "Printl(n)", "")
		env.Await(
			// Once we have gotten diagnostics for the change above, we should
			// satisfy the DiagnosticAtRegexp assertion.
			OnceMet(
				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
				env.DiagnosticAtRegexp("main.go", "Printl"),
			),
			// Assert that this test has sent no error logs to the client. This is not
			// strictly necessary for testing this regression, but is included here
			// as an example of using the NoErrorLogs() expectation. Feel free to
			// delete.
			NoErrorLogs(),
		)
	})
}

const onlyMod = `
-- go.mod --
module mod.com

go 1.12
`

func TestMissingImportDiagsClearOnFirstFile(t *testing.T) {
	t.Parallel()
	runner.Run(t, onlyMod, func(t *testing.T, env *Env) {
		env.CreateBuffer("main.go", `package main

func m() {
	log.Println()
}
`)
		env.Await(
			env.DiagnosticAtRegexp("main.go", "log"),
		)
		env.SaveBuffer("main.go")
		env.Await(
			EmptyDiagnostics("main.go"),
		)
	})
}

const brokenFile = `package main

const Foo = "abc
`

func TestDiagnosticErrorInNewFile(t *testing.T) {
	runner.Run(t, brokenFile, func(t *testing.T, env *Env) {
		env.CreateBuffer("broken.go", brokenFile)
		env.Await(env.DiagnosticAtRegexp("broken.go", "\"abc"))
	})
}

// badPackage contains a duplicate definition of the 'a' const.
const badPackage = `
-- go.mod --
module mod.com

go 1.12
-- a.go --
package consts

const a = 1
-- b.go --
package consts

const a = 2
`

func TestDiagnosticClearingOnEdit(t *testing.T) {
	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
		env.OpenFile("b.go")
		env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))

		// Fix the error by editing the const name in b.go to `b`.
		env.RegexpReplace("b.go", "(a) = 2", "b")
		env.Await(
			EmptyDiagnostics("a.go"),
			EmptyDiagnostics("b.go"),
		)
	})
}

func TestDiagnosticClearingOnDelete_Issue37049(t *testing.T) {
	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
		env.OpenFile("a.go")
		env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
		env.RemoveFileFromWorkspace("b.go")

		env.Await(EmptyDiagnostics("a.go"), EmptyDiagnostics("b.go"))
	})
}

func TestDiagnosticClearingOnClose(t *testing.T) {
	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
		env.CreateBuffer("c.go", `package consts

const a = 3`)
		env.Await(
			env.DiagnosticAtRegexp("a.go", "a = 1"),
			env.DiagnosticAtRegexp("b.go", "a = 2"),
			env.DiagnosticAtRegexp("c.go", "a = 3"))
		env.CloseBuffer("c.go")
		env.Await(
			env.DiagnosticAtRegexp("a.go", "a = 1"),
			env.DiagnosticAtRegexp("b.go", "a = 2"),
			EmptyDiagnostics("c.go"))
	})
}

// Tests golang/go#37978.
func TestIssue37978(t *testing.T) {
	runner.Run(t, exampleProgram, func(t *testing.T, env *Env) {
		// Create a new workspace-level directory and empty file.
		env.CreateBuffer("c/c.go", "")

		// Write the file contents with a missing import.
		env.EditBuffer("c/c.go", fake.Edit{
			Text: `package c

const a = http.MethodGet
`,
		})
		env.Await(
			env.DiagnosticAtRegexp("c/c.go", "http.MethodGet"),
		)
		// Save file, which will organize imports, adding the expected import.
		// Expect the diagnostics to clear.
		env.SaveBuffer("c/c.go")
		env.Await(
			EmptyDiagnostics("c/c.go"),
		)
	})
}

// TestNoMod confirms that gopls continues to work when a user adds a go.mod
// file to their workspace.
func TestNoMod(t *testing.T) {
	const noMod = `
-- main.go --
package main

import "mod.com/bob"

func main() {
	bob.Hello()
}
-- bob/bob.go --
package bob

func Hello() {
	var x int
}
`

	t.Run("manual", func(t *testing.T) {
		runner.Run(t, noMod, func(t *testing.T, env *Env) {
			env.Await(
				env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
			)
			env.CreateBuffer("go.mod", `module mod.com

	go 1.12
`)
			env.SaveBuffer("go.mod")
			env.Await(
				EmptyDiagnostics("main.go"),
				env.DiagnosticAtRegexp("bob/bob.go", "x"),
			)
		})
	})
	t.Run("initialized", func(t *testing.T) {
		runner.Run(t, noMod, func(t *testing.T, env *Env) {
			env.Await(
				env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
			)
			if err := env.W.RunGoCommand(env.Ctx, "mod", "init", "mod.com"); err != nil {
				t.Fatal(err)
			}
			env.Await(
				EmptyDiagnostics("main.go"),
				env.DiagnosticAtRegexp("bob/bob.go", "x"),
			)
		})
	})
}

// Tests golang/go#38267.
func TestIssue38267(t *testing.T) {
	const testPackage = `
-- go.mod --
module mod.com

go 1.12
-- lib.go --
package lib

func Hello(x string) {
	_ = x
}
-- lib_test.go --
package lib

import "testing"

type testStruct struct{
	name string
}

func TestHello(t *testing.T) {
	testStructs := []*testStruct{
		&testStruct{"hello"},
		&testStruct{"goodbye"},
	}
	for y := range testStructs {
		_ = y
	}
}
`

	runner.Run(t, testPackage, func(t *testing.T, env *Env) {
		env.OpenFile("lib_test.go")
		env.Await(
			DiagnosticAt("lib_test.go", 10, 2),
			DiagnosticAt("lib_test.go", 11, 2),
		)
		env.OpenFile("lib.go")
		env.RegexpReplace("lib.go", "_ = x", "var y int")
		env.Await(
			env.DiagnosticAtRegexp("lib.go", "y int"),
			EmptyDiagnostics("lib_test.go"),
		)
	})
}

// Tests golang/go#38328.
func TestPackageChange_Issue38328(t *testing.T) {
	const packageChange = `
-- go.mod --
module fake
-- a.go --
package foo
func main() {}
`
	runner.Run(t, packageChange, func(t *testing.T, env *Env) {
		env.OpenFile("a.go")
		env.RegexpReplace("a.go", "foo", "foox")
		env.Await(
			// When the bug reported in #38328 was present, we didn't get erroneous
			// file diagnostics until after the didChange message generated by the
			// package renaming was fully processed. Therefore, in order for this
			// test to actually exercise the bug, we must wait until that work has
			// completed.
			EmptyDiagnostics("a.go"),
			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
		)
	})
}

const testPackageWithRequire = `
-- go.mod --
module mod.com

go 1.12

require (
	foo.test v1.2.3
)
-- print.go --
package lib

import (
	"fmt"

	"foo.test/bar"
)

func PrintAnswer() {
	fmt.Printf("answer: %s", bar.Answer)
}
`

const testPackageWithRequireProxy = `
-- foo.test@v1.2.3/go.mod --
module foo.test

go 1.12
-- foo.test@v1.2.3/bar/const.go --
package bar

const Answer = 42
`

func TestResolveDiagnosticWithDownload(t *testing.T) {
	runner.Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
		env.OpenFile("print.go")
		// Check that gopackages correctly loaded this dependency. We should get a
		// diagnostic for the wrong formatting type.
		// TODO: we should be able to easily also match the diagnostic message.
		env.Await(env.DiagnosticAtRegexp("print.go", "fmt.Printf"))
	}, WithProxy(testPackageWithRequireProxy))
}

func TestMissingDependency(t *testing.T) {
	runner.Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
		env.OpenFile("print.go")
		env.Await(LogMatching(protocol.Error, "initial workspace load failed"))
	})
}

// Tests golang/go#36951.
func TestAdHocPackages_Issue36951(t *testing.T) {
	const adHoc = `
-- b/b.go --
package b

func Hello() {
	var x int
}
`
	runner.Run(t, adHoc, func(t *testing.T, env *Env) {
		env.OpenFile("b/b.go")
		env.Await(env.DiagnosticAtRegexp("b/b.go", "x"))
	})
}

// Tests golang/go#37984.
func TestNoGOPATH_Issue37984(t *testing.T) {
	const missingImport = `
-- main.go --
package main

func _() {
	fmt.Println("Hello World")
}
`
	runner.Run(t, missingImport, func(t *testing.T, env *Env) {
		env.OpenFile("main.go")
		env.Await(env.DiagnosticAtRegexp("main.go", "fmt"))
		if err := env.E.OrganizeImports(env.Ctx, "main.go"); err == nil {
			t.Fatalf("organize imports should fail with an empty GOPATH")
		}
	}, WithEnv("GOPATH="))
}

// Tests golang/go#38669.
func TestEqualInEnv_Issue38669(t *testing.T) {
	const missingImport = `
-- go.mod --
module mod.com

-- main.go --
package main

var _ = x.X
-- x/x.go --
package x

var X = 0
`
	runner.Run(t, missingImport, func(t *testing.T, env *Env) {
		env.OpenFile("main.go")
		env.OrganizeImports("main.go")
		env.Await(EmptyDiagnostics("main.go"))
	}, WithEnv("GOFLAGS=-tags=foo"))
}
<|endoftext|>


<commit_before>/* Copyright (C) 2015-2020 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2020년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package lib

import (
	"math"
	"sort"
)

func F합계(값_모음 []float64) float64 {
	합계 := 0.0

	for _, 값 := range 값_모음 {
		합계 += 값
	}

	return 합계
}

func F평균(값_모음 []float64) float64 {
	return F합계(값_모음) / float64(len(값_모음))
}

func F표준_편차(값_모음 []float64) float64 {
	평균 := F평균(값_모음)
	분산 := 0.0

	for i := 0; i < len(값_모음); i++ {
		분산 += math.Pow(값_모음[i]-평균, 2)
	}

	return math.Sqrt(분산 / float64(len(값_모음)))
}

func F최대값_실수(값_모음 []float64) float64 {
	최대값 := 값_모음[0]

	for i := 1; i < len(값_모음); i++ {
		최대값 = math.Max(최대값, 값_모음[i])
	}

	return 최대값
}

func F최소값_실수(값_모음 []float64) float64 {
	최소값 := 값_모음[0]

	for i := 1; i < len(값_모음); i++ {
		최소값 = math.Min(최소값, 값_모음[i])
	}

	return 최소값
}

func F중간값_실수(값_모음 []float64) float64 {
	sort.Float64s(값_모음)

	if len(값_모음)%2 == 1 {
		return 값_모음[(len(값_모음)-1)/2]
	} else {
		값1 := 값_모음[len(값_모음)/2-1]
		값2 := 값_모음[len(값_모음)/2]

		return (값1 + 값2) / 2
	}
}

func F최대값_정수64(값_모음 []int64) int64 {
	최대값 := 값_모음[0]

	for i := 1; i < len(값_모음); i++ {
		최대값 = F조건부_정수64(값_모음[i] > 최대값, 값_모음[i], 최대값)
	}

	return 최대값
}

func F최소값_정수64(값_모음 []int64) int64 {
	최소값 := 값_모음[0]

	for i := 1; i < len(값_모음); i++ {
		최소값 = F조건부_정수64(값_모음[i] < 최소값, 값_모음[i], 최소값)
	}

	return 최소값
}

func F중간값_정수64(값_모음 []int64) int64 {
	실수값_모음 := make([]float64, len(값_모음))

	for i, 값 := range 값_모음 {
		실수값_모음[i] = float64(값)
	}

	return int64(F중간값_실수(실수값_모음))
}
<commit_msg>F최대값_실수(), F최소값_실수() 예외 상황 처리 추가.<commit_after>/* Copyright (C) 2015-2020 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2020년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package lib

import (
	"math"
	"sort"
)

func F합계(값_모음 []float64) float64 {
	합계 := 0.0

	for _, 값 := range 값_모음 {
		합계 += 값
	}

	return 합계
}

func F평균(값_모음 []float64) float64 {
	return F합계(값_모음) / float64(len(값_모음))
}

func F표준_편차(값_모음 []float64) float64 {
	평균 := F평균(값_모음)
	분산 := 0.0

	for i := 0; i < len(값_모음); i++ {
		분산 += math.Pow(값_모음[i]-평균, 2)
	}

	return math.Sqrt(분산 / float64(len(값_모음)))
}

func F최대값_실수(값_모음 []float64) float64 {
	if len(값_모음) <= 0 {
		return 0
	}

	최대값 := 값_모음[0]

	for i := 1; i < len(값_모음); i++ {
		최대값 = math.Max(최대값, 값_모음[i])
	}

	return 최대값
}

func F최소값_실수(값_모음 []float64) float64 {
	if len(값_모음) <= 0 {
		return 0
	}

	최소값 := 값_모음[0]

	for i := 1; i < len(값_모음); i++ {
		최소값 = math.Min(최소값, 값_모음[i])
	}

	return 최소값
}

func F중간값_실수(값_모음 []float64) float64 {
	sort.Float64s(값_모음)

	if len(값_모음)%2 == 1 {
		return 값_모음[(len(값_모음)-1)/2]
	} else {
		값1 := 값_모음[len(값_모음)/2-1]
		값2 := 값_모음[len(값_모음)/2]

		return (값1 + 값2) / 2
	}
}

func F최대값_정수64(값_모음 []int64) int64 {
	최대값 := 값_모음[0]

	for i := 1; i < len(값_모음); i++ {
		최대값 = F조건부_정수64(값_모음[i] > 최대값, 값_모음[i], 최대값)
	}

	return 최대값
}

func F최소값_정수64(값_모음 []int64) int64 {
	최소값 := 값_모음[0]

	for i := 1; i < len(값_모음); i++ {
		최소값 = F조건부_정수64(값_모음[i] < 최소값, 값_모음[i], 최소값)
	}

	return 최소값
}

func F중간값_정수64(값_모음 []int64) int64 {
	실수값_모음 := make([]float64, len(값_모음))

	for i, 값 := range 값_모음 {
		실수값_모음[i] = float64(값)
	}

	return int64(F중간값_실수(실수값_모음))
}
<|endoftext|>


<commit_before>// Copyright 2017 The Prometheus 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 promhttp

import (
	"net/http"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

// The RoundTripperFunc type is an adapter to allow the use of ordinary
// functions as RoundTrippers. If f is a function with the appropriate
// signature, RountTripperFunc(f) is a RoundTripper that calls f.
type RoundTripperFunc func(req *http.Request) (*http.Response, error)

// RoundTrip implements the RoundTripper interface.
func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
	return rt(r)
}

// InstrumentRoundTripperInFlight is a middleware that wraps the provided
// http.RoundTripper. It sets the provided prometheus.Gauge to the number of
// requests currently handled by the wrapped http.RoundTripper.
//
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc {
	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
		gauge.Inc()
		defer gauge.Dec()
		return next.RoundTrip(r)
	})
}

// InstrumentRoundTripperCounter is a middleware that wraps the provided
// http.RoundTripper to observe the request result with the provided CounterVec.
// The CounterVec must have zero, one, or two non-const non-curried labels. For
// those, the only allowed label names are "code" and "method". The function
// panics otherwise. Partitioning of the CounterVec happens by HTTP status code
// and/or HTTP method if the respective instance label names are present in the
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
//
// If the wrapped RoundTripper panics or returns a non-nil error, the Counter
// is not incremented.
//
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc {
	code, method := checkLabels(counter)

	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
		resp, err := next.RoundTrip(r)
		if err == nil {
			counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc()
		}
		return resp, err
	})
}

// InstrumentRoundTripperDuration is a middleware that wraps the provided
// http.RoundTripper to observe the request duration with the provided ObserverVec.
// The ObserverVec must have zero, one, or two labels. The only allowed label
// names are "code" and "method". The function panics if any other instance
// labels are provided. The Observe method of the Observer in the ObserverVec
// is called with the request duration in seconds. Partitioning happens by HTTP
// status code and/or HTTP method if the respective instance label names are
// present in the ObserverVec. For unpartitioned observations, use an
// ObserverVec with zero labels. Note that partitioning of Histograms is
// expensive and should be used judiciously.
//
// If the wrapped RoundTripper panics or returns a non-nil error, no values are
// reported.
//
// Note that this method is only guaranteed to never observe negative durations
// if used with Go1.9+.
func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc {
	code, method := checkLabels(obs)

	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
		start := time.Now()
		resp, err := next.RoundTrip(r)
		if err == nil {
			obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds())
		}
		return resp, err
	})
}
<commit_msg>Minor doc comment fix<commit_after>// Copyright 2017 The Prometheus 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 promhttp

import (
	"net/http"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

// The RoundTripperFunc type is an adapter to allow the use of ordinary
// functions as RoundTrippers. If f is a function with the appropriate
// signature, RountTripperFunc(f) is a RoundTripper that calls f.
type RoundTripperFunc func(req *http.Request) (*http.Response, error)

// RoundTrip implements the RoundTripper interface.
func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
	return rt(r)
}

// InstrumentRoundTripperInFlight is a middleware that wraps the provided
// http.RoundTripper. It sets the provided prometheus.Gauge to the number of
// requests currently handled by the wrapped http.RoundTripper.
//
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc {
	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
		gauge.Inc()
		defer gauge.Dec()
		return next.RoundTrip(r)
	})
}

// InstrumentRoundTripperCounter is a middleware that wraps the provided
// http.RoundTripper to observe the request result with the provided CounterVec.
// The CounterVec must have zero, one, or two non-const non-curried labels. For
// those, the only allowed label names are "code" and "method". The function
// panics otherwise. Partitioning of the CounterVec happens by HTTP status code
// and/or HTTP method if the respective instance label names are present in the
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
//
// If the wrapped RoundTripper panics or returns a non-nil error, the Counter
// is not incremented.
//
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc {
	code, method := checkLabels(counter)

	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
		resp, err := next.RoundTrip(r)
		if err == nil {
			counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc()
		}
		return resp, err
	})
}

// InstrumentRoundTripperDuration is a middleware that wraps the provided
// http.RoundTripper to observe the request duration with the provided
// ObserverVec.  The ObserverVec must have zero, one, or two non-const
// non-curried labels. For those, the only allowed label names are "code" and
// "method". The function panics otherwise. The Observe method of the Observer
// in the ObserverVec is called with the request duration in
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
// respective instance label names are present in the ObserverVec. For
// unpartitioned observations, use an ObserverVec with zero labels. Note that
// partitioning of Histograms is expensive and should be used judiciously.
//
// If the wrapped RoundTripper panics or returns a non-nil error, no values are
// reported.
//
// Note that this method is only guaranteed to never observe negative durations
// if used with Go1.9+.
func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc {
	code, method := checkLabels(obs)

	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
		start := time.Now()
		resp, err := next.RoundTrip(r)
		if err == nil {
			obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds())
		}
		return resp, err
	})
}
<|endoftext|>


<commit_before>package registry

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/docker/registry"
	"github.com/flynn/docker-utils/version"
)

type Registry struct {
	Version string
	Path    string
	Info    registry.RegistryInfo
}

func (r *Registry) Init() error {
	p, err := filepath.Abs(r.Path)
	if err != nil {
		return err
	}
	r.Path = p

	r.Info.Version = version.VERSION
	r.Info.Standalone = true

	if _, err := os.Stat(r.Path); os.IsNotExist(err) {
		err = os.Mkdir(r.Path, 0755)
		if err != nil {
			return err
		}
	}
	if r.Version == "" {
		r.Version = "v1"
	}

	for _, dir := range []string{"repositories/library", "images"} {
		err = os.MkdirAll(filepath.Join(r.Path, r.Version, dir), 0755)
		if err != nil {
			return err
		}
	}

	if _, err = os.Stat(filepath.Join(r.Path, r.Version, "_ping")); os.IsNotExist(err) {
		fh, err := os.Create(filepath.Join(r.Path, r.Version, "_ping"))
		if err != nil {
			return err
		}
		buf, err := json.Marshal(r.Info)
		if err != nil {
			return err
		}
		_, err = fh.Write(buf)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r Registry) EnsureRepoReady(name string) error {
	if err := os.MkdirAll(r.RepositoryPath(name), 0755); err != nil {
		return err
	}
	if strings.Count(name, "/") == 0 {
		if err := os.Symlink(r.RepositoryPath(name), r.RepositoryPath("library/"+name)); err != nil {
			return err
		}
	}
	return nil
}

func (r Registry) CreateAncestry(hashid string) error {
	hashes := []string{}
	// Unmarshal the json for the layer, get the parent

	thisHash := hashid
	for {
		imageJson, err := ioutil.ReadFile(r.JsonFileName(thisHash))
		if err != nil {
			return err
		}
		imageData := ImageMetadata{}
		if err = json.Unmarshal(imageJson, &imageData); err != nil {
			return err
		}
		if len(imageData.Parent) == 0 {
			break
		}
		hashes = append(hashes, imageData.Parent)
		thisHash = imageData.Parent
	}

	ancestry_fh, err := os.Create(r.AncestryFileName(hashid))
	if err != nil {
		return err
	}
	defer ancestry_fh.Close()
	hashesJson, err := json.Marshal(hashes)
	if err != nil {
		return err
	}
	if _, err = ancestry_fh.Write(hashesJson); err != nil {
		return err
	}
	return nil
}

func (r Registry) HasRepository(name string) bool {
	var hasImages, hasTags bool
	if r.Version == "v1" {
		if s, err := os.Stat(r.ImagesFileName(name)); err == nil && s.Mode().IsRegular() {
			hasImages = true
		}
		if s, err := os.Stat(r.TagsFileName(name)); err == nil && s.Mode().IsRegular() {
			hasTags = true
		}
	}
	return hasImages && hasTags
}

func (r Registry) HasImage(hashid string) bool {
	var hasJson, hasLayer bool
	if r.Version == "v1" {
		if s, err := os.Stat(r.JsonFileName(hashid)); err == nil && s.Mode().IsRegular() {
			hasJson = true
		}
		if s, err := os.Stat(r.LayerFileName(hashid)); err == nil && s.Mode().IsRegular() {
			hasLayer = true
		}
	}
	return hasJson && hasLayer
}

func (r Registry) LayerTarsum(hashid string) (string, error) {
	buf, err := ioutil.ReadFile(r.TarsumFileName(hashid))
	if err != nil {
		return "", err
	}
	return string(buf), nil
}

func (r Registry) RepositoryPath(name string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "repositories", name)
	}
	return ""
}

func (r Registry) ImagesFileName(name string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "repositories", name, "images")
	}
	return ""
}

func (r Registry) TagsFileName(name string) string {
	if r.Version == "v1" {
		if strings.Count(name, "/") == 0 {
			name = "library/" + name
		}
		return filepath.Join(r.Path, r.Version, "repositories", name, "tags")
	}
	return ""
}

func (r Registry) JsonFileName(hashid string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "images", hashid, "json")
	}
	return ""
}

func (r Registry) LayerFileName(hashid string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "images", hashid, "layer")
	}
	return ""
}

func (r Registry) TarsumFileName(hashid string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "images", hashid, "tarsum")
	}
	return ""
}

func (r Registry) AncestryFileName(hashid string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "images", hashid, "ancestry")
	}
	return ""
}

// for the ./images/ file
type ImageMetadata struct {
	Id     string `json:"id"`
	Parent string `json:"parent"`
}

// for the ./repositories file
type Image struct {
	Checksum string `json:"checksum,omitempty"`
	Id       string `json:"id"`
}

// for the ./repositories file
type Tag struct {
	Layer string `json:"layer"`
	Name  string `json:"name"`
}

func TagsMap(tags []Tag) map[string]string {
	output := map[string]string{}
	for i := range tags {
		output[tags[i].Name] = tags[i].Layer
	}
	return output
}
<commit_msg>registry: Include image ID in ancestry<commit_after>package registry

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/docker/registry"
	"github.com/flynn/docker-utils/version"
)

type Registry struct {
	Version string
	Path    string
	Info    registry.RegistryInfo
}

func (r *Registry) Init() error {
	p, err := filepath.Abs(r.Path)
	if err != nil {
		return err
	}
	r.Path = p

	r.Info.Version = version.VERSION
	r.Info.Standalone = true

	if _, err := os.Stat(r.Path); os.IsNotExist(err) {
		err = os.Mkdir(r.Path, 0755)
		if err != nil {
			return err
		}
	}
	if r.Version == "" {
		r.Version = "v1"
	}

	for _, dir := range []string{"repositories/library", "images"} {
		err = os.MkdirAll(filepath.Join(r.Path, r.Version, dir), 0755)
		if err != nil {
			return err
		}
	}

	if _, err = os.Stat(filepath.Join(r.Path, r.Version, "_ping")); os.IsNotExist(err) {
		fh, err := os.Create(filepath.Join(r.Path, r.Version, "_ping"))
		if err != nil {
			return err
		}
		buf, err := json.Marshal(r.Info)
		if err != nil {
			return err
		}
		_, err = fh.Write(buf)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r Registry) EnsureRepoReady(name string) error {
	if err := os.MkdirAll(r.RepositoryPath(name), 0755); err != nil {
		return err
	}
	if strings.Count(name, "/") == 0 {
		if err := os.Symlink(r.RepositoryPath(name), r.RepositoryPath("library/"+name)); err != nil {
			return err
		}
	}
	return nil
}

func (r Registry) CreateAncestry(hashid string) error {
	// the ancestry starts at the given ID and ends at the scratch layer
	hashes := []string{hashid}

	thisHash := hashid
	for {
		// Unmarshal the json for the layer, get the parent
		imageJson, err := ioutil.ReadFile(r.JsonFileName(thisHash))
		if err != nil {
			return err
		}
		imageData := ImageMetadata{}
		if err = json.Unmarshal(imageJson, &imageData); err != nil {
			return err
		}
		if len(imageData.Parent) == 0 {
			break
		}
		hashes = append(hashes, imageData.Parent)
		thisHash = imageData.Parent
	}

	ancestry_fh, err := os.Create(r.AncestryFileName(hashid))
	if err != nil {
		return err
	}
	defer ancestry_fh.Close()
	hashesJson, err := json.Marshal(hashes)
	if err != nil {
		return err
	}
	if _, err = ancestry_fh.Write(hashesJson); err != nil {
		return err
	}
	return nil
}

func (r Registry) HasRepository(name string) bool {
	var hasImages, hasTags bool
	if r.Version == "v1" {
		if s, err := os.Stat(r.ImagesFileName(name)); err == nil && s.Mode().IsRegular() {
			hasImages = true
		}
		if s, err := os.Stat(r.TagsFileName(name)); err == nil && s.Mode().IsRegular() {
			hasTags = true
		}
	}
	return hasImages && hasTags
}

func (r Registry) HasImage(hashid string) bool {
	var hasJson, hasLayer bool
	if r.Version == "v1" {
		if s, err := os.Stat(r.JsonFileName(hashid)); err == nil && s.Mode().IsRegular() {
			hasJson = true
		}
		if s, err := os.Stat(r.LayerFileName(hashid)); err == nil && s.Mode().IsRegular() {
			hasLayer = true
		}
	}
	return hasJson && hasLayer
}

func (r Registry) LayerTarsum(hashid string) (string, error) {
	buf, err := ioutil.ReadFile(r.TarsumFileName(hashid))
	if err != nil {
		return "", err
	}
	return string(buf), nil
}

func (r Registry) RepositoryPath(name string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "repositories", name)
	}
	return ""
}

func (r Registry) ImagesFileName(name string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "repositories", name, "images")
	}
	return ""
}

func (r Registry) TagsFileName(name string) string {
	if r.Version == "v1" {
		if strings.Count(name, "/") == 0 {
			name = "library/" + name
		}
		return filepath.Join(r.Path, r.Version, "repositories", name, "tags")
	}
	return ""
}

func (r Registry) JsonFileName(hashid string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "images", hashid, "json")
	}
	return ""
}

func (r Registry) LayerFileName(hashid string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "images", hashid, "layer")
	}
	return ""
}

func (r Registry) TarsumFileName(hashid string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "images", hashid, "tarsum")
	}
	return ""
}

func (r Registry) AncestryFileName(hashid string) string {
	if r.Version == "v1" {
		return filepath.Join(r.Path, r.Version, "images", hashid, "ancestry")
	}
	return ""
}

// for the ./images/ file
type ImageMetadata struct {
	Id     string `json:"id"`
	Parent string `json:"parent"`
}

// for the ./repositories file
type Image struct {
	Checksum string `json:"checksum,omitempty"`
	Id       string `json:"id"`
}

// for the ./repositories file
type Tag struct {
	Layer string `json:"layer"`
	Name  string `json:"name"`
}

func TagsMap(tags []Tag) map[string]string {
	output := map[string]string{}
	for i := range tags {
		output[tags[i].Name] = tags[i].Layer
	}
	return output
}
<|endoftext|>


<commit_before>package models

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"time"
)

type Collection struct {
	Id       string `json:"id"`
	Title    string `json:"title" form:"title"`
	Subtitle string `json:"subtitle" form:"subtitle"`
	Author   string `json:"author"`
	Date     string `json:"date"`
}

func AllCollections() ([]Collection, error) {
	result, err := db.Do("hsize", h_collection)
	if err != nil {
		return []Collection{}, err
	}
	size, _ := strconv.Atoi(result[1])
	if size == 0 {
		return []Collection{}, nil
	}

	result, err = db.Do("hscan", h_collection, "", "", size)
	if err != nil {
		return []Collection{}, err
	}
	status := result[0]
	if status != "ok" {
		return []Collection{}, errors.New(status)
	}

	collections := []Collection{}
	for i := 2; i < len(result); i += 2 {
		c := Collection{}
		_ = json.Unmarshal([]byte(result[i]), &c)
		collections = append(collections, c)
	}

	return collections, nil
}

func CollectionsByUser(name string) ([]Collection, error) {
	result, err := db.Do("zsize", "blog_"+name+"_collection")
	if err != nil {
		return []Collection{}, err
	}
	size, _ := strconv.Atoi(result[1])
	if size == 0 {
		return []Collection{}, nil
	}

	result, err = db.Do("zscan", "blog_"+name+"_collection", "", "", "", size)
	if err != nil {
		return []Collection{}, err
	}
	status := result[0]
	if status != "ok" {
		return []Collection{}, errors.New(status)
	}

	cids := make([]string, 0)
	for i := 1; i < len(result); i += 2 {
		cids = append(cids, result[i])
	}

	result, err = db.Do("multi_hget", h_collection, cids)
	if err != nil {
		return []Collection{}, err
	}
	status = result[0]
	if status != "ok" {
		return []Collection{}, errors.New(status)
	}

	collections := []Collection{}
	for i := 2; i < len(result); i += 2 {
		c := Collection{}
		_ = json.Unmarshal([]byte(result[i]), &c)
		collections = append(collections, c)
	}

	return collections, nil
}

func CreateCollection(c Collection) (string, error) {
	c.Id = Hash(c.Title)
	c.Date = time.Now().Format(time.RFC3339)
	fmt.Println("Create collection: ", c)

	_, err := CollectionById(c.Id)
	fmt.Println("Check duplication: ", err)
	if err.Error() != "not_found" || err == nil {
		if err != nil {
			return "", err
		} else {
			return "", errors.New("collection exists")
		}
	}

	cbytes, _ := json.Marshal(c)
	result, err := db.Do("hset", h_collection, c.Id, string(cbytes))
	if err != nil {
		return "", err
	}
	status := result[0]
	if status != "ok" {
		return "", errors.New(status)
	}
	result, err = db.Do("zset", "blog_"+c.Author+"_collection", c.Id, time.Now().Unix())
	return c.Id, nil
}

func UpdateCollection(c Collection) (string, error) {
	oid := c.Id
	nid := Hash(c.Title)

	_, err := db.Do("hdel", h_collection, oid)
	if err != nil {
		return "", err
	}

	c.Id = nid

	cbytes, _ := json.Marshal(c)
	result, err := db.Do("hset", h_collection, c.Id, string(cbytes))
	if err != nil {
		return "", err
	}
	status := result[0]
	if status != "ok" {
		return "", errors.New(status)
	}

	return c.Id, nil
}

func CollectionById(id string) (Collection, error) {
	result, err := db.Do("hget", h_collection, id)
	if err != nil {
		panic(err)
		return Collection{}, err
	}
	fmt.Println(result)
	status := result[0]
	if status != "ok" {
		return Collection{}, errors.New(status)
	}

	c := Collection{}
	json.Unmarshal([]byte(result[1]), &c)
	return c, nil
}

func DeleteCollection(id string) error {
	result, err := db.Do("hdel", h_collection, id)
	if err != nil {
		return err
	}
	status := result[0]
	if status != "ok" {
		return errors.New(status)
	}

	return nil
}
<commit_msg>Fix date display bug.<commit_after>package models

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"time"
)

type Collection struct {
	Id       string `json:"id"`
	Title    string `json:"title" form:"title"`
	Subtitle string `json:"subtitle" form:"subtitle"`
	Author   string `json:"author"`
	Date     string `json:"date"`
}

func AllCollections() ([]Collection, error) {
	result, err := db.Do("hsize", h_collection)
	if err != nil {
		return []Collection{}, err
	}
	size, _ := strconv.Atoi(result[1])
	if size == 0 {
		return []Collection{}, nil
	}

	result, err = db.Do("hscan", h_collection, "", "", size)
	if err != nil {
		return []Collection{}, err
	}
	status := result[0]
	if status != "ok" {
		return []Collection{}, errors.New(status)
	}

	collections := []Collection{}
	for i := 2; i < len(result); i += 2 {
		c := Collection{}
		_ = json.Unmarshal([]byte(result[i]), &c)
		t, _ := time.Parse(time.RFC3339, c.Date)
		c.Date = t.Format(time.ANSIC)
		collections = append(collections, c)
	}

	return collections, nil
}

func CollectionsByUser(name string) ([]Collection, error) {
	result, err := db.Do("zsize", "blog_"+name+"_collection")
	if err != nil {
		return []Collection{}, err
	}
	size, _ := strconv.Atoi(result[1])
	if size == 0 {
		return []Collection{}, nil
	}

	result, err = db.Do("zscan", "blog_"+name+"_collection", "", "", "", size)
	if err != nil {
		return []Collection{}, err
	}
	status := result[0]
	if status != "ok" {
		return []Collection{}, errors.New(status)
	}

	cids := make([]string, 0)
	for i := 1; i < len(result); i += 2 {
		cids = append(cids, result[i])
	}

	result, err = db.Do("multi_hget", h_collection, cids)
	if err != nil {
		return []Collection{}, err
	}
	status = result[0]
	if status != "ok" {
		return []Collection{}, errors.New(status)
	}

	collections := []Collection{}
	for i := 2; i < len(result); i += 2 {
		c := Collection{}
		_ = json.Unmarshal([]byte(result[i]), &c)
		collections = append(collections, c)
	}

	return collections, nil
}

func CreateCollection(c Collection) (string, error) {
	c.Id = Hash(c.Title)
	c.Date = time.Now().Format(time.RFC3339)
	fmt.Println("Create collection: ", c)

	_, err := CollectionById(c.Id)
	fmt.Println("Check duplication: ", err)
	if err.Error() != "not_found" || err == nil {
		if err != nil {
			return "", err
		} else {
			return "", errors.New("collection exists")
		}
	}

	cbytes, _ := json.Marshal(c)
	result, err := db.Do("hset", h_collection, c.Id, string(cbytes))
	if err != nil {
		return "", err
	}
	status := result[0]
	if status != "ok" {
		return "", errors.New(status)
	}
	result, err = db.Do("zset", "blog_"+c.Author+"_collection", c.Id, time.Now().Unix())
	return c.Id, nil
}

func UpdateCollection(c Collection) (string, error) {
	oid := c.Id
	nid := Hash(c.Title)

	_, err := db.Do("hdel", h_collection, oid)
	if err != nil {
		return "", err
	}

	c.Id = nid

	cbytes, _ := json.Marshal(c)
	result, err := db.Do("hset", h_collection, c.Id, string(cbytes))
	if err != nil {
		return "", err
	}
	status := result[0]
	if status != "ok" {
		return "", errors.New(status)
	}

	return c.Id, nil
}

func CollectionById(id string) (Collection, error) {
	result, err := db.Do("hget", h_collection, id)
	if err != nil {
		panic(err)
		return Collection{}, err
	}
	fmt.Println(result)
	status := result[0]
	if status != "ok" {
		return Collection{}, errors.New(status)
	}

	c := Collection{}
	json.Unmarshal([]byte(result[1]), &c)
	return c, nil
}

func DeleteCollection(id string) error {
	result, err := db.Do("hdel", h_collection, id)
	if err != nil {
		return err
	}
	status := result[0]
	if status != "ok" {
		return errors.New(status)
	}

	return nil
}
<|endoftext|>


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

package models

import "fmt"

// IssueList defines a list of issues
type IssueList []*Issue

func (issues IssueList) getRepoIDs() []int64 {
	repoIDs := make(map[int64]struct{}, len(issues))
	for _, issue := range issues {
		if _, ok := repoIDs[issue.RepoID]; !ok {
			repoIDs[issue.RepoID] = struct{}{}
		}
	}
	return keysInt64(repoIDs)
}

func (issues IssueList) loadRepositories(e Engine) ([]*Repository, error) {
	if len(issues) == 0 {
		return nil, nil
	}

	repoIDs := issues.getRepoIDs()
	repoMaps := make(map[int64]*Repository, len(repoIDs))
	err := e.
		In("id", repoIDs).
		Find(&repoMaps)
	if err != nil {
		return nil, fmt.Errorf("find repository: %v", err)
	}

	for _, issue := range issues {
		issue.Repo = repoMaps[issue.RepoID]
	}
	return valuesRepository(repoMaps), nil
}

// LoadRepositories loads issues' all repositories
func (issues IssueList) LoadRepositories() ([]*Repository, error) {
	return issues.loadRepositories(x)
}

func (issues IssueList) getPosterIDs() []int64 {
	posterIDs := make(map[int64]struct{}, len(issues))
	for _, issue := range issues {
		if _, ok := posterIDs[issue.PosterID]; !ok {
			posterIDs[issue.PosterID] = struct{}{}
		}
	}
	return keysInt64(posterIDs)
}

func (issues IssueList) loadPosters(e Engine) error {
	if len(issues) == 0 {
		return nil
	}

	posterIDs := issues.getPosterIDs()
	posterMaps := make(map[int64]*User, len(posterIDs))
	err := e.
		In("id", posterIDs).
		Find(&posterMaps)
	if err != nil {
		return err
	}

	for _, issue := range issues {
		if issue.PosterID <= 0 {
			continue
		}
		var ok bool
		if issue.Poster, ok = posterMaps[issue.PosterID]; !ok {
			issue.Poster = NewGhostUser()
		}
	}
	return nil
}

func (issues IssueList) getIssueIDs() []int64 {
	var ids = make([]int64, 0, len(issues))
	for _, issue := range issues {
		ids = append(ids, issue.ID)
	}
	return ids
}

func (issues IssueList) loadLabels(e Engine) error {
	if len(issues) == 0 {
		return nil
	}

	type LabelIssue struct {
		Label      *Label      `xorm:"extends"`
		IssueLabel *IssueLabel `xorm:"extends"`
	}

	var issueLabels = make(map[int64][]*Label, len(issues)*3)
	rows, err := e.Table("label").
		Join("LEFT", "issue_label", "issue_label.label_id = label.id").
		In("issue_label.issue_id", issues.getIssueIDs()).
		Asc("label.name").
		Rows(new(LabelIssue))
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		var labelIssue LabelIssue
		err = rows.Scan(&labelIssue)
		if err != nil {
			return err
		}
		issueLabels[labelIssue.IssueLabel.IssueID] = append(issueLabels[labelIssue.IssueLabel.IssueID], labelIssue.Label)
	}

	for _, issue := range issues {
		issue.Labels = issueLabels[issue.ID]
	}
	return nil
}

func (issues IssueList) getMilestoneIDs() []int64 {
	var ids = make(map[int64]struct{}, len(issues))
	for _, issue := range issues {
		if _, ok := ids[issue.MilestoneID]; !ok {
			ids[issue.MilestoneID] = struct{}{}
		}
	}
	return keysInt64(ids)
}

func (issues IssueList) loadMilestones(e Engine) error {
	milestoneIDs := issues.getMilestoneIDs()
	if len(milestoneIDs) == 0 {
		return nil
	}

	milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
	err := e.
		In("id", milestoneIDs).
		Find(&milestoneMaps)
	if err != nil {
		return err
	}

	for _, issue := range issues {
		issue.Milestone = milestoneMaps[issue.MilestoneID]
	}
	return nil
}

func (issues IssueList) loadAssignees(e Engine) error {
	if len(issues) == 0 {
		return nil
	}

	type AssigneeIssue struct {
		IssueAssignee *IssueAssignees `xorm:"extends"`
		Assignee      *User           `xorm:"extends"`
	}

	var assignees = make(map[int64][]*User, len(issues))
	rows, err := e.Table("issue_assignees").
		Join("INNER", "`user`", "`user`.id = `issue_assignees`.assignee_id").
		In("`issue_assignees`.issue_id", issues.getIssueIDs()).
		Rows(new(AssigneeIssue))
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		var assigneeIssue AssigneeIssue
		err = rows.Scan(&assigneeIssue)
		if err != nil {
			return err
		}

		assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee)
	}

	for _, issue := range issues {
		issue.Assignees = assignees[issue.ID]
	}
	return nil
}

func (issues IssueList) getPullIssueIDs() []int64 {
	var ids = make([]int64, 0, len(issues))
	for _, issue := range issues {
		if issue.IsPull && issue.PullRequest == nil {
			ids = append(ids, issue.ID)
		}
	}
	return ids
}

func (issues IssueList) loadPullRequests(e Engine) error {
	issuesIDs := issues.getPullIssueIDs()
	if len(issuesIDs) == 0 {
		return nil
	}

	pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
	rows, err := e.
		In("issue_id", issuesIDs).
		Rows(new(PullRequest))
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		var pr PullRequest
		err = rows.Scan(&pr)
		if err != nil {
			return err
		}
		pullRequestMaps[pr.IssueID] = &pr
	}

	for _, issue := range issues {
		issue.PullRequest = pullRequestMaps[issue.ID]
	}
	return nil
}

func (issues IssueList) loadAttachments(e Engine) (err error) {
	if len(issues) == 0 {
		return nil
	}

	var attachments = make(map[int64][]*Attachment, len(issues))
	rows, err := e.Table("attachment").
		Join("INNER", "issue", "issue.id = attachment.issue_id").
		In("issue.id", issues.getIssueIDs()).
		Rows(new(Attachment))
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		var attachment Attachment
		err = rows.Scan(&attachment)
		if err != nil {
			return err
		}
		attachments[attachment.IssueID] = append(attachments[attachment.IssueID], &attachment)
	}

	for _, issue := range issues {
		issue.Attachments = attachments[issue.ID]
	}
	return nil
}

func (issues IssueList) loadComments(e Engine) (err error) {
	if len(issues) == 0 {
		return nil
	}

	var comments = make(map[int64][]*Comment, len(issues))
	rows, err := e.Table("comment").
		Join("INNER", "issue", "issue.id = comment.issue_id").
		In("issue.id", issues.getIssueIDs()).
		Rows(new(Comment))
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		var comment Comment
		err = rows.Scan(&comment)
		if err != nil {
			return err
		}
		comments[comment.IssueID] = append(comments[comment.IssueID], &comment)
	}

	for _, issue := range issues {
		issue.Comments = comments[issue.ID]
	}
	return nil
}

func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) {
	type totalTimesByIssue struct {
		IssueID int64
		Time    int64
	}
	if len(issues) == 0 {
		return nil
	}
	var trackedTimes = make(map[int64]int64, len(issues))

	var ids = make([]int64, 0, len(issues))
	for _, issue := range issues {
		if issue.Repo.IsTimetrackerEnabled() {
			ids = append(ids, issue.ID)
		}
	}

	// select issue_id, sum(time) from tracked_time where issue_id in (<issue ids in current page>) group by issue_id
	rows, err := e.Table("tracked_time").
		Select("issue_id, sum(time) as time").
		In("issue_id", ids).
		GroupBy("issue_id").
		Rows(new(totalTimesByIssue))
	if err != nil {
		return err
	}

	defer rows.Close()

	for rows.Next() {
		var totalTime totalTimesByIssue
		err = rows.Scan(&totalTime)
		if err != nil {
			return err
		}
		trackedTimes[totalTime.IssueID] = totalTime.Time
	}

	for _, issue := range issues {
		issue.TotalTrackedTime = trackedTimes[issue.ID]
	}
	return nil
}

// loadAttributes loads all attributes, expect for attachments and comments
func (issues IssueList) loadAttributes(e Engine) (err error) {
	if _, err = issues.loadRepositories(e); err != nil {
		return
	}

	if err = issues.loadPosters(e); err != nil {
		return
	}

	if err = issues.loadLabels(e); err != nil {
		return
	}

	if err = issues.loadMilestones(e); err != nil {
		return
	}

	if err = issues.loadAssignees(e); err != nil {
		return
	}

	if err = issues.loadPullRequests(e); err != nil {
		return
	}

	if err = issues.loadTotalTrackedTimes(e); err != nil {
		return
	}

	return nil
}

// LoadAttributes loads attributes of the issues, except for attachments and
// comments
func (issues IssueList) LoadAttributes() error {
	return issues.loadAttributes(x)
}

// LoadAttachments loads attachments
func (issues IssueList) LoadAttachments() error {
	return issues.loadAttachments(x)
}

// LoadComments loads comments
func (issues IssueList) LoadComments() error {
	return issues.loadComments(x)
}
<commit_msg>fix bugs when too many IN variables (#4594)<commit_after>// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import "fmt"

// IssueList defines a list of issues
type IssueList []*Issue

const (
	// default variables number on IN () in SQL
	defaultMaxInSize = 50
)

func (issues IssueList) getRepoIDs() []int64 {
	repoIDs := make(map[int64]struct{}, len(issues))
	for _, issue := range issues {
		if _, ok := repoIDs[issue.RepoID]; !ok {
			repoIDs[issue.RepoID] = struct{}{}
		}
	}
	return keysInt64(repoIDs)
}

func (issues IssueList) loadRepositories(e Engine) ([]*Repository, error) {
	if len(issues) == 0 {
		return nil, nil
	}

	repoIDs := issues.getRepoIDs()
	repoMaps := make(map[int64]*Repository, len(repoIDs))
	var left = len(repoIDs)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}
		err := e.
			In("id", repoIDs[:limit]).
			Find(&repoMaps)
		if err != nil {
			return nil, fmt.Errorf("find repository: %v", err)
		}
		left = left - limit
		repoIDs = repoIDs[limit:]
	}

	for _, issue := range issues {
		issue.Repo = repoMaps[issue.RepoID]
	}
	return valuesRepository(repoMaps), nil
}

// LoadRepositories loads issues' all repositories
func (issues IssueList) LoadRepositories() ([]*Repository, error) {
	return issues.loadRepositories(x)
}

func (issues IssueList) getPosterIDs() []int64 {
	posterIDs := make(map[int64]struct{}, len(issues))
	for _, issue := range issues {
		if _, ok := posterIDs[issue.PosterID]; !ok {
			posterIDs[issue.PosterID] = struct{}{}
		}
	}
	return keysInt64(posterIDs)
}

func (issues IssueList) loadPosters(e Engine) error {
	if len(issues) == 0 {
		return nil
	}

	posterIDs := issues.getPosterIDs()
	posterMaps := make(map[int64]*User, len(posterIDs))
	var left = len(posterIDs)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}
		err := e.
			In("id", posterIDs[:limit]).
			Find(&posterMaps)
		if err != nil {
			return err
		}
		left = left - limit
		posterIDs = posterIDs[limit:]
	}

	for _, issue := range issues {
		if issue.PosterID <= 0 {
			continue
		}
		var ok bool
		if issue.Poster, ok = posterMaps[issue.PosterID]; !ok {
			issue.Poster = NewGhostUser()
		}
	}
	return nil
}

func (issues IssueList) getIssueIDs() []int64 {
	var ids = make([]int64, 0, len(issues))
	for _, issue := range issues {
		ids = append(ids, issue.ID)
	}
	return ids
}

func (issues IssueList) loadLabels(e Engine) error {
	if len(issues) == 0 {
		return nil
	}

	type LabelIssue struct {
		Label      *Label      `xorm:"extends"`
		IssueLabel *IssueLabel `xorm:"extends"`
	}

	var issueLabels = make(map[int64][]*Label, len(issues)*3)
	var issueIDs = issues.getIssueIDs()
	var left = len(issueIDs)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}
		rows, err := e.Table("label").
			Join("LEFT", "issue_label", "issue_label.label_id = label.id").
			In("issue_label.issue_id", issueIDs[:limit]).
			Asc("label.name").
			Rows(new(LabelIssue))
		if err != nil {
			return err
		}

		for rows.Next() {
			var labelIssue LabelIssue
			err = rows.Scan(&labelIssue)
			if err != nil {
				rows.Close()
				return err
			}
			issueLabels[labelIssue.IssueLabel.IssueID] = append(issueLabels[labelIssue.IssueLabel.IssueID], labelIssue.Label)
		}
		rows.Close()
		left = left - limit
		issueIDs = issueIDs[limit:]
	}

	for _, issue := range issues {
		issue.Labels = issueLabels[issue.ID]
	}
	return nil
}

func (issues IssueList) getMilestoneIDs() []int64 {
	var ids = make(map[int64]struct{}, len(issues))
	for _, issue := range issues {
		if _, ok := ids[issue.MilestoneID]; !ok {
			ids[issue.MilestoneID] = struct{}{}
		}
	}
	return keysInt64(ids)
}

func (issues IssueList) loadMilestones(e Engine) error {
	milestoneIDs := issues.getMilestoneIDs()
	if len(milestoneIDs) == 0 {
		return nil
	}

	milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
	var left = len(milestoneIDs)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}
		err := e.
			In("id", milestoneIDs[:limit]).
			Find(&milestoneMaps)
		if err != nil {
			return err
		}
		left = left - limit
		milestoneIDs = milestoneIDs[limit:]
	}

	for _, issue := range issues {
		issue.Milestone = milestoneMaps[issue.MilestoneID]
	}
	return nil
}

func (issues IssueList) loadAssignees(e Engine) error {
	if len(issues) == 0 {
		return nil
	}

	type AssigneeIssue struct {
		IssueAssignee *IssueAssignees `xorm:"extends"`
		Assignee      *User           `xorm:"extends"`
	}

	var assignees = make(map[int64][]*User, len(issues))
	var issueIDs = issues.getIssueIDs()
	var left = len(issueIDs)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}
		rows, err := e.Table("issue_assignees").
			Join("INNER", "`user`", "`user`.id = `issue_assignees`.assignee_id").
			In("`issue_assignees`.issue_id", issueIDs[:limit]).
			Rows(new(AssigneeIssue))
		if err != nil {
			return err
		}

		for rows.Next() {
			var assigneeIssue AssigneeIssue
			err = rows.Scan(&assigneeIssue)
			if err != nil {
				rows.Close()
				return err
			}

			assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee)
		}
		rows.Close()

		left = left - limit
		issueIDs = issueIDs[limit:]
	}

	for _, issue := range issues {
		issue.Assignees = assignees[issue.ID]
	}
	return nil
}

func (issues IssueList) getPullIssueIDs() []int64 {
	var ids = make([]int64, 0, len(issues))
	for _, issue := range issues {
		if issue.IsPull && issue.PullRequest == nil {
			ids = append(ids, issue.ID)
		}
	}
	return ids
}

func (issues IssueList) loadPullRequests(e Engine) error {
	issuesIDs := issues.getPullIssueIDs()
	if len(issuesIDs) == 0 {
		return nil
	}

	pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
	var left = len(issuesIDs)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}
		rows, err := e.
			In("issue_id", issuesIDs[:limit]).
			Rows(new(PullRequest))
		if err != nil {
			return err
		}

		for rows.Next() {
			var pr PullRequest
			err = rows.Scan(&pr)
			if err != nil {
				rows.Close()
				return err
			}
			pullRequestMaps[pr.IssueID] = &pr
		}

		rows.Close()
		left = left - limit
		issuesIDs = issuesIDs[limit:]
	}

	for _, issue := range issues {
		issue.PullRequest = pullRequestMaps[issue.ID]
	}
	return nil
}

func (issues IssueList) loadAttachments(e Engine) (err error) {
	if len(issues) == 0 {
		return nil
	}

	var attachments = make(map[int64][]*Attachment, len(issues))
	var issuesIDs = issues.getIssueIDs()
	var left = len(issuesIDs)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}
		rows, err := e.Table("attachment").
			Join("INNER", "issue", "issue.id = attachment.issue_id").
			In("issue.id", issuesIDs[:limit]).
			Rows(new(Attachment))
		if err != nil {
			return err
		}

		for rows.Next() {
			var attachment Attachment
			err = rows.Scan(&attachment)
			if err != nil {
				rows.Close()
				return err
			}
			attachments[attachment.IssueID] = append(attachments[attachment.IssueID], &attachment)
		}

		rows.Close()
		left = left - limit
		issuesIDs = issuesIDs[limit:]
	}

	for _, issue := range issues {
		issue.Attachments = attachments[issue.ID]
	}
	return nil
}

func (issues IssueList) loadComments(e Engine) (err error) {
	if len(issues) == 0 {
		return nil
	}

	var comments = make(map[int64][]*Comment, len(issues))
	var issuesIDs = issues.getIssueIDs()
	var left = len(issuesIDs)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}
		rows, err := e.Table("comment").
			Join("INNER", "issue", "issue.id = comment.issue_id").
			In("issue.id", issuesIDs[:limit]).
			Rows(new(Comment))
		if err != nil {
			return err
		}

		for rows.Next() {
			var comment Comment
			err = rows.Scan(&comment)
			if err != nil {
				rows.Close()
				return err
			}
			comments[comment.IssueID] = append(comments[comment.IssueID], &comment)
		}
		rows.Close()
		left = left - limit
		issuesIDs = issuesIDs[limit:]
	}

	for _, issue := range issues {
		issue.Comments = comments[issue.ID]
	}
	return nil
}

func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) {
	type totalTimesByIssue struct {
		IssueID int64
		Time    int64
	}
	if len(issues) == 0 {
		return nil
	}
	var trackedTimes = make(map[int64]int64, len(issues))

	var ids = make([]int64, 0, len(issues))
	for _, issue := range issues {
		if issue.Repo.IsTimetrackerEnabled() {
			ids = append(ids, issue.ID)
		}
	}

	var left = len(ids)
	for left > 0 {
		var limit = defaultMaxInSize
		if left < limit {
			limit = left
		}

		// select issue_id, sum(time) from tracked_time where issue_id in (<issue ids in current page>) group by issue_id
		rows, err := e.Table("tracked_time").
			Select("issue_id, sum(time) as time").
			In("issue_id", ids[:limit]).
			GroupBy("issue_id").
			Rows(new(totalTimesByIssue))
		if err != nil {
			return err
		}

		for rows.Next() {
			var totalTime totalTimesByIssue
			err = rows.Scan(&totalTime)
			if err != nil {
				rows.Close()
				return err
			}
			trackedTimes[totalTime.IssueID] = totalTime.Time
		}
		rows.Close()
		left = left - limit
		ids = ids[limit:]
	}

	for _, issue := range issues {
		issue.TotalTrackedTime = trackedTimes[issue.ID]
	}
	return nil
}

// loadAttributes loads all attributes, expect for attachments and comments
func (issues IssueList) loadAttributes(e Engine) (err error) {
	if _, err = issues.loadRepositories(e); err != nil {
		return
	}

	if err = issues.loadPosters(e); err != nil {
		return
	}

	if err = issues.loadLabels(e); err != nil {
		return
	}

	if err = issues.loadMilestones(e); err != nil {
		return
	}

	if err = issues.loadAssignees(e); err != nil {
		return
	}

	if err = issues.loadPullRequests(e); err != nil {
		return
	}

	if err = issues.loadTotalTrackedTimes(e); err != nil {
		return
	}

	return nil
}

// LoadAttributes loads attributes of the issues, except for attachments and
// comments
func (issues IssueList) LoadAttributes() error {
	return issues.loadAttributes(x)
}

// LoadAttachments loads attachments
func (issues IssueList) LoadAttachments() error {
	return issues.loadAttachments(x)
}

// LoadComments loads comments
func (issues IssueList) LoadComments() error {
	return issues.loadComments(x)
}
<|endoftext|>


<commit_before>// Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"

	"github.com/Unknwon/com"

	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/markup"
	"code.gitea.io/gitea/modules/setting"
)

func (issue *Issue) mailSubject() string {
	return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Title, issue.Index)
}

// mailIssueCommentToParticipants can be used for both new issue creation and comment.
// This function sends two list of emails:
// 1. Repository watchers and users who are participated in comments.
// 2. Users who are not in 1. but get mentioned in current issue/comment.
func mailIssueCommentToParticipants(e Engine, issue *Issue, doer *User, content string, comment *Comment, mentions []string) error {
	if !setting.Service.EnableNotifyMail {
		return nil
	}

	watchers, err := getWatchers(e, issue.RepoID)
	if err != nil {
		return fmt.Errorf("getWatchers [repo_id: %d]: %v", issue.RepoID, err)
	}
	participants, err := getParticipantsByIssueID(e, issue.ID)
	if err != nil {
		return fmt.Errorf("getParticipantsByIssueID [issue_id: %d]: %v", issue.ID, err)
	}

	// In case the issue poster is not watching the repository and is active,
	// even if we have duplicated in watchers, can be safely filtered out.
	poster, err := getUserByID(e, issue.PosterID)
	if err != nil {
		return fmt.Errorf("GetUserByID [%d]: %v", issue.PosterID, err)
	}
	if issue.PosterID != doer.ID && poster.IsActive && !poster.ProhibitLogin {
		participants = append(participants, issue.Poster)
	}

	// Assignees must receive any communications
	assignees, err := getAssigneesByIssue(e, issue)
	if err != nil {
		return err
	}

	for _, assignee := range assignees {
		if assignee.ID != doer.ID {
			participants = append(participants, assignee)
		}
	}

	tos := make([]string, 0, len(watchers)) // List of email addresses.
	names := make([]string, 0, len(watchers))
	for i := range watchers {
		if watchers[i].UserID == doer.ID {
			continue
		}

		to, err := getUserByID(e, watchers[i].UserID)
		if err != nil {
			return fmt.Errorf("GetUserByID [%d]: %v", watchers[i].UserID, err)
		}
		if to.IsOrganization() {
			continue
		}

		tos = append(tos, to.Email)
		names = append(names, to.Name)
	}
	for i := range participants {
		if participants[i].ID == doer.ID {
			continue
		} else if com.IsSliceContainsStr(names, participants[i].Name) {
			continue
		}

		tos = append(tos, participants[i].Email)
		names = append(names, participants[i].Name)
	}

	for _, to := range tos {
		SendIssueCommentMail(issue, doer, content, comment, []string{to})
	}

	// Mail mentioned people and exclude watchers.
	names = append(names, doer.Name)
	tos = make([]string, 0, len(mentions)) // list of user names.
	for i := range mentions {
		if com.IsSliceContainsStr(names, mentions[i]) {
			continue
		}

		tos = append(tos, mentions[i])
	}

	emails := getUserEmailsByNames(e, tos)

	for _, to := range emails {
		SendIssueMentionMail(issue, doer, content, comment, []string{to})
	}

	return nil
}

// MailParticipants sends new issue thread created emails to repository watchers
// and mentioned people.
func (issue *Issue) MailParticipants() (err error) {
	return issue.mailParticipants(x)
}

func (issue *Issue) mailParticipants(e Engine) (err error) {
	mentions := markup.FindAllMentions(issue.Content)
	if err = UpdateIssueMentions(e, issue.ID, mentions); err != nil {
		return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
	}

	if err = mailIssueCommentToParticipants(e, issue, issue.Poster, issue.Content, nil, mentions); err != nil {
		log.Error(4, "mailIssueCommentToParticipants: %v", err)
	}

	return nil
}
<commit_msg>Ensure issue.Poster is loaded in mailIssueCommentToParticipants (#5891)<commit_after>// Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"

	"github.com/Unknwon/com"

	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/markup"
	"code.gitea.io/gitea/modules/setting"
)

func (issue *Issue) mailSubject() string {
	return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Title, issue.Index)
}

// mailIssueCommentToParticipants can be used for both new issue creation and comment.
// This function sends two list of emails:
// 1. Repository watchers and users who are participated in comments.
// 2. Users who are not in 1. but get mentioned in current issue/comment.
func mailIssueCommentToParticipants(e Engine, issue *Issue, doer *User, content string, comment *Comment, mentions []string) error {
	if !setting.Service.EnableNotifyMail {
		return nil
	}

	watchers, err := getWatchers(e, issue.RepoID)
	if err != nil {
		return fmt.Errorf("getWatchers [repo_id: %d]: %v", issue.RepoID, err)
	}
	participants, err := getParticipantsByIssueID(e, issue.ID)
	if err != nil {
		return fmt.Errorf("getParticipantsByIssueID [issue_id: %d]: %v", issue.ID, err)
	}

	// In case the issue poster is not watching the repository and is active,
	// even if we have duplicated in watchers, can be safely filtered out.
	err = issue.loadPoster(e)
	if err != nil {
		return fmt.Errorf("GetUserByID [%d]: %v", issue.PosterID, err)
	}
	if issue.PosterID != doer.ID && issue.Poster.IsActive && !issue.Poster.ProhibitLogin {
		participants = append(participants, issue.Poster)
	}

	// Assignees must receive any communications
	assignees, err := getAssigneesByIssue(e, issue)
	if err != nil {
		return err
	}

	for _, assignee := range assignees {
		if assignee.ID != doer.ID {
			participants = append(participants, assignee)
		}
	}

	tos := make([]string, 0, len(watchers)) // List of email addresses.
	names := make([]string, 0, len(watchers))
	for i := range watchers {
		if watchers[i].UserID == doer.ID {
			continue
		}

		to, err := getUserByID(e, watchers[i].UserID)
		if err != nil {
			return fmt.Errorf("GetUserByID [%d]: %v", watchers[i].UserID, err)
		}
		if to.IsOrganization() {
			continue
		}

		tos = append(tos, to.Email)
		names = append(names, to.Name)
	}
	for i := range participants {
		if participants[i].ID == doer.ID {
			continue
		} else if com.IsSliceContainsStr(names, participants[i].Name) {
			continue
		}

		tos = append(tos, participants[i].Email)
		names = append(names, participants[i].Name)
	}

	for _, to := range tos {
		SendIssueCommentMail(issue, doer, content, comment, []string{to})
	}

	// Mail mentioned people and exclude watchers.
	names = append(names, doer.Name)
	tos = make([]string, 0, len(mentions)) // list of user names.
	for i := range mentions {
		if com.IsSliceContainsStr(names, mentions[i]) {
			continue
		}

		tos = append(tos, mentions[i])
	}

	emails := getUserEmailsByNames(e, tos)

	for _, to := range emails {
		SendIssueMentionMail(issue, doer, content, comment, []string{to})
	}

	return nil
}

// MailParticipants sends new issue thread created emails to repository watchers
// and mentioned people.
func (issue *Issue) MailParticipants() (err error) {
	return issue.mailParticipants(x)
}

func (issue *Issue) mailParticipants(e Engine) (err error) {
	mentions := markup.FindAllMentions(issue.Content)
	if err = UpdateIssueMentions(e, issue.ID, mentions); err != nil {
		return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
	}

	if err = mailIssueCommentToParticipants(e, issue, issue.Poster, issue.Content, nil, mentions); err != nil {
		log.Error(4, "mailIssueCommentToParticipants: %v", err)
	}

	return nil
}
<|endoftext|>


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

package models

import (
	"os"
	"testing"

	"code.gitea.io/gitea/modules/setting"

	"github.com/Unknwon/com"
	"github.com/go-xorm/core"
	"github.com/go-xorm/xorm"
	"github.com/stretchr/testify/assert"
	"gopkg.in/testfixtures.v2"
)

// NonexistentID an ID that will never exist
const NonexistentID = 9223372036854775807

// CreateTestEngine create in-memory sqlite database for unit tests
// Any package that calls this must import github.com/mattn/go-sqlite3
func CreateTestEngine(fixturesDir string) error {
	var err error
	x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
	if err != nil {
		return err
	}
	x.SetMapper(core.GonicMapper{})
	if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
		return err
	}
	x.ShowSQL(true)

	return InitFixtures(&testfixtures.SQLite{}, fixturesDir)
}

// PrepareTestDatabase load test fixtures into test database
func PrepareTestDatabase() error {
	return LoadFixtures()
}

func prepareTestEnv(t testing.TB) {
	assert.NoError(t, PrepareTestDatabase())
	assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
	assert.NoError(t, com.CopyDir("../integrations/gitea-repositories-meta", setting.RepoRootPath))
}

type testCond struct {
	query interface{}
	args  []interface{}
}

// Cond create a condition with arguments for a test
func Cond(query interface{}, args ...interface{}) interface{} {
	return &testCond{query: query, args: args}
}

func whereConditions(sess *xorm.Session, conditions []interface{}) {
	for _, condition := range conditions {
		switch cond := condition.(type) {
		case *testCond:
			sess.Where(cond.query, cond.args...)
		default:
			sess.Where(cond)
		}
	}
}

func loadBeanIfExists(bean interface{}, conditions ...interface{}) (bool, error) {
	sess := x.NewSession()
	defer sess.Close()
	whereConditions(sess, conditions)
	return sess.Get(bean)
}

// BeanExists for testing, check if a bean exists
func BeanExists(t *testing.T, bean interface{}, conditions ...interface{}) bool {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	return exists
}

// AssertExistsAndLoadBean assert that a bean exists and load it from the test
// database
func AssertExistsAndLoadBean(t *testing.T, bean interface{}, conditions ...interface{}) interface{} {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.True(t, exists,
		"Expected to find %+v (of type %T, with conditions %+v), but did not",
		bean, bean, conditions)
	return bean
}

// GetCount get the count of a bean
func GetCount(t *testing.T, bean interface{}, conditions ...interface{}) int {
	sess := x.NewSession()
	defer sess.Close()
	whereConditions(sess, conditions)
	count, err := sess.Count(bean)
	assert.NoError(t, err)
	return int(count)
}

// AssertNotExistsBean assert that a bean does not exist in the test database
func AssertNotExistsBean(t *testing.T, bean interface{}, conditions ...interface{}) {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.False(t, exists)
}

// AssertSuccessfulInsert assert that beans is successfully inserted
func AssertSuccessfulInsert(t *testing.T, beans ...interface{}) {
	_, err := x.Insert(beans...)
	assert.NoError(t, err)
}

// AssertCount assert the count of a bean
func AssertCount(t *testing.T, bean interface{}, expected interface{}) {
	assert.EqualValues(t, expected, GetCount(t, bean))
}

// AssertInt64InRange assert value is in range [low, high]
func AssertInt64InRange(t *testing.T, low, high, value int64) {
	assert.True(t, value >= low && value <= high,
		"Expected value in range [%d, %d], found %d", low, high, value)
}
<commit_msg>Don't print SQL in unit tests (#2995)<commit_after>// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"os"
	"testing"

	"code.gitea.io/gitea/modules/setting"

	"github.com/Unknwon/com"
	"github.com/go-xorm/core"
	"github.com/go-xorm/xorm"
	"github.com/stretchr/testify/assert"
	"gopkg.in/testfixtures.v2"
)

// NonexistentID an ID that will never exist
const NonexistentID = 9223372036854775807

// CreateTestEngine create in-memory sqlite database for unit tests
// Any package that calls this must import github.com/mattn/go-sqlite3
func CreateTestEngine(fixturesDir string) error {
	var err error
	x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
	if err != nil {
		return err
	}
	x.SetMapper(core.GonicMapper{})
	if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
		return err
	}
	switch os.Getenv("GITEA_UNIT_TESTS_VERBOSE") {
	case "true", "1":
		x.ShowSQL(true)
	}

	return InitFixtures(&testfixtures.SQLite{}, fixturesDir)
}

// PrepareTestDatabase load test fixtures into test database
func PrepareTestDatabase() error {
	return LoadFixtures()
}

func prepareTestEnv(t testing.TB) {
	assert.NoError(t, PrepareTestDatabase())
	assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
	assert.NoError(t, com.CopyDir("../integrations/gitea-repositories-meta", setting.RepoRootPath))
}

type testCond struct {
	query interface{}
	args  []interface{}
}

// Cond create a condition with arguments for a test
func Cond(query interface{}, args ...interface{}) interface{} {
	return &testCond{query: query, args: args}
}

func whereConditions(sess *xorm.Session, conditions []interface{}) {
	for _, condition := range conditions {
		switch cond := condition.(type) {
		case *testCond:
			sess.Where(cond.query, cond.args...)
		default:
			sess.Where(cond)
		}
	}
}

func loadBeanIfExists(bean interface{}, conditions ...interface{}) (bool, error) {
	sess := x.NewSession()
	defer sess.Close()
	whereConditions(sess, conditions)
	return sess.Get(bean)
}

// BeanExists for testing, check if a bean exists
func BeanExists(t *testing.T, bean interface{}, conditions ...interface{}) bool {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	return exists
}

// AssertExistsAndLoadBean assert that a bean exists and load it from the test
// database
func AssertExistsAndLoadBean(t *testing.T, bean interface{}, conditions ...interface{}) interface{} {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.True(t, exists,
		"Expected to find %+v (of type %T, with conditions %+v), but did not",
		bean, bean, conditions)
	return bean
}

// GetCount get the count of a bean
func GetCount(t *testing.T, bean interface{}, conditions ...interface{}) int {
	sess := x.NewSession()
	defer sess.Close()
	whereConditions(sess, conditions)
	count, err := sess.Count(bean)
	assert.NoError(t, err)
	return int(count)
}

// AssertNotExistsBean assert that a bean does not exist in the test database
func AssertNotExistsBean(t *testing.T, bean interface{}, conditions ...interface{}) {
	exists, err := loadBeanIfExists(bean, conditions...)
	assert.NoError(t, err)
	assert.False(t, exists)
}

// AssertSuccessfulInsert assert that beans is successfully inserted
func AssertSuccessfulInsert(t *testing.T, beans ...interface{}) {
	_, err := x.Insert(beans...)
	assert.NoError(t, err)
}

// AssertCount assert the count of a bean
func AssertCount(t *testing.T, bean interface{}, expected interface{}) {
	assert.EqualValues(t, expected, GetCount(t, bean))
}

// AssertInt64InRange assert value is in range [low, high]
func AssertInt64InRange(t *testing.T, low, high, value int64) {
	assert.True(t, value >= low && value <= high,
		"Expected value in range [%d, %d], found %d", low, high, value)
}
<|endoftext|>


<commit_before>package worker

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"
	"time"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/concourse/atc"
	"github.com/concourse/baggageclaim"
	"github.com/pivotal-golang/lager"
)

const ephemeralPropertyName = "concourse:ephemeral"
const volumePropertyName = "concourse:volumes"
const volumeMountsPropertyName = "concourse:volume-mounts"

type releasable interface {
	Release(*time.Duration)
}

type gardenContainerSpecFactory struct {
	logger             lager.Logger
	baggageclaimClient baggageclaim.Client
	imageFetcher       ImageFetcher
	volumeMounts       map[string]string
	volumeHandles      []string
	user               string
	releaseAfterCreate []releasable
	db                 GardenWorkerDB
}

func NewGardenContainerSpecFactory(logger lager.Logger, baggageclaimClient baggageclaim.Client, imageFetcher ImageFetcher, db GardenWorkerDB) gardenContainerSpecFactory {
	return gardenContainerSpecFactory{
		logger:             logger,
		baggageclaimClient: baggageclaimClient,
		imageFetcher:       imageFetcher,
		volumeMounts:       map[string]string{},
		volumeHandles:      nil,
		releaseAfterCreate: []releasable{},
		db:                 db,
	}
}

func (factory *gardenContainerSpecFactory) BuildContainerSpec(
	spec ContainerSpec,
	resourceTypes []atc.WorkerResourceType,
	workerTags atc.Tags,
	cancel <-chan os.Signal,
	delegate ImageFetchingDelegate,
	id Identifier,
	metadata Metadata,
	workerClient Client,
	customTypes atc.ResourceTypes,
) (garden.ContainerSpec, error) {
	var err error

	resourceTypeContainerSpec, ok := spec.(ResourceTypeContainerSpec)
	if ok {
		for _, customType := range customTypes {
			if customType.Name == resourceTypeContainerSpec.Type {
				customTypes = customTypes.Without(resourceTypeContainerSpec.Type)

				resourceTypeContainerSpec.ImageResourcePointer = &atc.TaskImageConfig{
					Source: customType.Source,
					Type:   customType.Type,
				}

				spec = resourceTypeContainerSpec
			}
		}
	}

	imageResourceConfig, hasImageResource := spec.ImageResource()
	var gardenSpec garden.ContainerSpec
	if hasImageResource {
		image, err := factory.imageFetcher.FetchImage(
			factory.logger,
			imageResourceConfig,
			cancel,
			id,
			metadata,
			delegate,
			workerClient,
			workerTags,
			customTypes,
		)
		if err != nil {
			return garden.ContainerSpec{}, err
		}

		imageVolume := image.Volume()

		factory.volumeHandles = append(factory.volumeHandles, imageVolume.Handle())
		factory.releaseAfterCreate = append(factory.releaseAfterCreate, image)
		factory.user = image.Metadata().User

		gardenSpec = garden.ContainerSpec{
			Properties: garden.Properties{},
			RootFSPath: path.Join(imageVolume.Path(), "rootfs"),
			Env:        image.Metadata().Env,
		}
	} else {
		gardenSpec = garden.ContainerSpec{
			Properties: garden.Properties{},
		}
	}

	switch s := spec.(type) {
	case ResourceTypeContainerSpec:
		gardenSpec, err = factory.BuildResourceContainerSpec(s, gardenSpec, resourceTypes)
	case TaskContainerSpec:
		gardenSpec, err = factory.BuildTaskContainerSpec(s, gardenSpec, cancel, delegate, id, metadata, workerClient)
	default:
		return garden.ContainerSpec{}, fmt.Errorf("unknown container spec type: %T (%#v)", s, s)
	}
	if err != nil {
		return garden.ContainerSpec{}, err
	}

	if len(factory.volumeHandles) > 0 {
		volumesJSON, err := json.Marshal(factory.volumeHandles)
		if err != nil {
			return garden.ContainerSpec{}, err
		}

		gardenSpec.Properties[volumePropertyName] = string(volumesJSON)

		mountsJSON, err := json.Marshal(factory.volumeMounts)
		if err != nil {
			return garden.ContainerSpec{}, err
		}

		gardenSpec.Properties[volumeMountsPropertyName] = string(mountsJSON)
	}

	gardenSpec.Properties["user"] = factory.user

	return gardenSpec, nil
}

func (factory *gardenContainerSpecFactory) BuildResourceContainerSpec(
	spec ResourceTypeContainerSpec,
	gardenSpec garden.ContainerSpec,
	resourceTypes []atc.WorkerResourceType,
) (garden.ContainerSpec, error) {
	if len(spec.Mounts) > 0 && spec.Cache.Volume != nil {
		return gardenSpec, errors.New("a container may not have mounts and a cache")
	}

	gardenSpec.Privileged = true
	gardenSpec.Env = append(gardenSpec.Env, spec.Env...)

	if spec.Ephemeral {
		gardenSpec.Properties[ephemeralPropertyName] = "true"
	}

	if spec.Cache.Volume != nil && spec.Cache.MountPath != "" {
		gardenSpec.BindMounts = []garden.BindMount{
			{
				SrcPath: spec.Cache.Volume.Path(),
				DstPath: spec.Cache.MountPath,
				Mode:    garden.BindMountModeRW,
			},
		}

		factory.volumeHandles = append(factory.volumeHandles, spec.Cache.Volume.Handle())
		factory.volumeMounts[spec.Cache.Volume.Handle()] = spec.Cache.MountPath
	}

	var err error
	gardenSpec, err = factory.createVolumes(gardenSpec, spec.Mounts)
	if err != nil {
		return gardenSpec, err
	}

	if spec.ImageResourcePointer == nil {
		for _, t := range resourceTypes {
			if t.Type == spec.Type {
				gardenSpec.RootFSPath = t.Image
				return gardenSpec, nil
			}
		}

		return gardenSpec, ErrUnsupportedResourceType
	}

	return gardenSpec, nil
}

func (factory *gardenContainerSpecFactory) BuildTaskContainerSpec(
	spec TaskContainerSpec,
	gardenSpec garden.ContainerSpec,
	cancel <-chan os.Signal,
	delegate ImageFetchingDelegate,
	id Identifier,
	metadata Metadata,
	workerClient Client,
) (garden.ContainerSpec, error) {
	if spec.ImageResourcePointer == nil {
		gardenSpec.RootFSPath = spec.Image
	}

	gardenSpec.Privileged = spec.Privileged

	var err error
	gardenSpec, err = factory.createVolumes(gardenSpec, spec.Inputs)
	if err != nil {
		return gardenSpec, err
	}

	for _, mount := range spec.Outputs {
		volume := mount.Volume
		gardenSpec.BindMounts = append(gardenSpec.BindMounts, garden.BindMount{
			SrcPath: volume.Path(),
			DstPath: mount.MountPath,
			Mode:    garden.BindMountModeRW,
		})

		factory.volumeHandles = append(factory.volumeHandles, volume.Handle())
		factory.volumeMounts[volume.Handle()] = mount.MountPath
	}

	return gardenSpec, nil
}

func (factory *gardenContainerSpecFactory) ReleaseVolumes() {
	for _, cow := range factory.releaseAfterCreate {
		cow.Release(nil)
	}
}

func (factory *gardenContainerSpecFactory) createVolumes(containerSpec garden.ContainerSpec, mounts []VolumeMount) (garden.ContainerSpec, error) {
	for _, mount := range mounts {
		cowVolume, err := factory.baggageclaimClient.CreateVolume(factory.logger, baggageclaim.VolumeSpec{
			Strategy: baggageclaim.COWStrategy{
				Parent: mount.Volume,
			},
			Privileged: containerSpec.Privileged,
			TTL:        VolumeTTL,
		})
		if err != nil {
			return containerSpec, err
		}

		err = factory.db.InsertCOWVolume(mount.Volume.Handle(), cowVolume.Handle(), VolumeTTL)
		if err != nil {
			return containerSpec, err
		}

		factory.releaseAfterCreate = append(factory.releaseAfterCreate, cowVolume)

		containerSpec.BindMounts = append(containerSpec.BindMounts, garden.BindMount{
			SrcPath: cowVolume.Path(),
			DstPath: mount.MountPath,
			Mode:    garden.BindMountModeRW,
		})

		factory.volumeHandles = append(factory.volumeHandles, cowVolume.Handle())
		factory.volumeMounts[cowVolume.Handle()] = mount.MountPath

		factory.logger.Info("created-cow-volume", lager.Data{
			"original-volume-handle": mount.Volume.Handle(),
			"cow-volume-handle":      cowVolume.Handle(),
		})
	}

	return containerSpec, nil
}
<commit_msg>remove Build...ContainerSpec methods<commit_after>package worker

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"
	"time"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/concourse/atc"
	"github.com/concourse/baggageclaim"
	"github.com/pivotal-golang/lager"
)

const ephemeralPropertyName = "concourse:ephemeral"
const volumePropertyName = "concourse:volumes"
const volumeMountsPropertyName = "concourse:volume-mounts"

type releasable interface {
	Release(*time.Duration)
}

type gardenContainerSpecFactory struct {
	logger             lager.Logger
	baggageclaimClient baggageclaim.Client
	imageFetcher       ImageFetcher
	volumeMounts       map[string]string
	volumeHandles      []string
	user               string
	releaseAfterCreate []releasable
	db                 GardenWorkerDB
}

func NewGardenContainerSpecFactory(logger lager.Logger, baggageclaimClient baggageclaim.Client, imageFetcher ImageFetcher, db GardenWorkerDB) gardenContainerSpecFactory {
	return gardenContainerSpecFactory{
		logger:             logger,
		baggageclaimClient: baggageclaimClient,
		imageFetcher:       imageFetcher,
		volumeMounts:       map[string]string{},
		volumeHandles:      nil,
		releaseAfterCreate: []releasable{},
		db:                 db,
	}
}

func (factory *gardenContainerSpecFactory) BuildContainerSpec(
	spec ContainerSpec,
	resourceTypes []atc.WorkerResourceType,
	workerTags atc.Tags,
	cancel <-chan os.Signal,
	delegate ImageFetchingDelegate,
	id Identifier,
	metadata Metadata,
	workerClient Client,
	customTypes atc.ResourceTypes,
) (garden.ContainerSpec, error) {
	var err error

	resourceTypeContainerSpec, ok := spec.(ResourceTypeContainerSpec)
	if ok {
		for _, customType := range customTypes {
			if customType.Name == resourceTypeContainerSpec.Type {
				customTypes = customTypes.Without(resourceTypeContainerSpec.Type)

				resourceTypeContainerSpec.ImageResourcePointer = &atc.TaskImageConfig{
					Source: customType.Source,
					Type:   customType.Type,
				}

				spec = resourceTypeContainerSpec
			}
		}
	}

	imageResourceConfig, hasImageResource := spec.ImageResource()
	var gardenSpec garden.ContainerSpec
	if hasImageResource {
		image, err := factory.imageFetcher.FetchImage(
			factory.logger,
			imageResourceConfig,
			cancel,
			id,
			metadata,
			delegate,
			workerClient,
			workerTags,
			customTypes,
		)
		if err != nil {
			return garden.ContainerSpec{}, err
		}

		imageVolume := image.Volume()

		factory.volumeHandles = append(factory.volumeHandles, imageVolume.Handle())
		factory.releaseAfterCreate = append(factory.releaseAfterCreate, image)
		factory.user = image.Metadata().User

		gardenSpec = garden.ContainerSpec{
			Properties: garden.Properties{},
			RootFSPath: path.Join(imageVolume.Path(), "rootfs"),
			Env:        image.Metadata().Env,
		}
	} else {
		gardenSpec = garden.ContainerSpec{
			Properties: garden.Properties{},
		}
	}

dance:
	switch s := spec.(type) {
	case ResourceTypeContainerSpec:
		if len(s.Mounts) > 0 && s.Cache.Volume != nil {
			return gardenSpec, errors.New("a container may not have mounts and a cache")
		}

		gardenSpec.Privileged = true
		gardenSpec.Env = append(gardenSpec.Env, s.Env...)

		if s.Ephemeral {
			gardenSpec.Properties[ephemeralPropertyName] = "true"
		}

		if s.Cache.Volume != nil && s.Cache.MountPath != "" {
			gardenSpec.BindMounts = []garden.BindMount{
				{
					SrcPath: s.Cache.Volume.Path(),
					DstPath: s.Cache.MountPath,
					Mode:    garden.BindMountModeRW,
				},
			}

			factory.volumeHandles = append(factory.volumeHandles, s.Cache.Volume.Handle())
			factory.volumeMounts[s.Cache.Volume.Handle()] = s.Cache.MountPath
		}

		var err error
		gardenSpec, err = factory.createVolumes(gardenSpec, s.Mounts)
		if err != nil {
			return garden.ContainerSpec{}, err
		}

		if s.ImageResourcePointer == nil {
			for _, t := range resourceTypes {
				if t.Type == s.Type {
					gardenSpec.RootFSPath = t.Image
					break dance
				}
			}

			return garden.ContainerSpec{}, ErrUnsupportedResourceType
		}

		break dance
	case TaskContainerSpec:
		if s.ImageResourcePointer == nil {
			gardenSpec.RootFSPath = s.Image
		}

		gardenSpec.Privileged = s.Privileged

		var err error
		gardenSpec, err = factory.createVolumes(gardenSpec, s.Inputs)
		if err != nil {
			return garden.ContainerSpec{}, err
		}

		for _, mount := range s.Outputs {
			volume := mount.Volume
			gardenSpec.BindMounts = append(gardenSpec.BindMounts, garden.BindMount{
				SrcPath: volume.Path(),
				DstPath: mount.MountPath,
				Mode:    garden.BindMountModeRW,
			})

			factory.volumeHandles = append(factory.volumeHandles, volume.Handle())
			factory.volumeMounts[volume.Handle()] = mount.MountPath
		}

		break dance
	default:
		return garden.ContainerSpec{}, fmt.Errorf("unknown container spec type: %T (%#v)", s, s)
	}
	if err != nil {
		return garden.ContainerSpec{}, err
	}

	if len(factory.volumeHandles) > 0 {
		volumesJSON, err := json.Marshal(factory.volumeHandles)
		if err != nil {
			return garden.ContainerSpec{}, err
		}

		gardenSpec.Properties[volumePropertyName] = string(volumesJSON)

		mountsJSON, err := json.Marshal(factory.volumeMounts)
		if err != nil {
			return garden.ContainerSpec{}, err
		}

		gardenSpec.Properties[volumeMountsPropertyName] = string(mountsJSON)
	}

	gardenSpec.Properties["user"] = factory.user

	return gardenSpec, nil
}

func (factory *gardenContainerSpecFactory) ReleaseVolumes() {
	for _, cow := range factory.releaseAfterCreate {
		cow.Release(nil)
	}
}

func (factory *gardenContainerSpecFactory) createVolumes(containerSpec garden.ContainerSpec, mounts []VolumeMount) (garden.ContainerSpec, error) {
	for _, mount := range mounts {
		cowVolume, err := factory.baggageclaimClient.CreateVolume(factory.logger, baggageclaim.VolumeSpec{
			Strategy: baggageclaim.COWStrategy{
				Parent: mount.Volume,
			},
			Privileged: containerSpec.Privileged,
			TTL:        VolumeTTL,
		})
		if err != nil {
			return containerSpec, err
		}

		err = factory.db.InsertCOWVolume(mount.Volume.Handle(), cowVolume.Handle(), VolumeTTL)
		if err != nil {
			return containerSpec, err
		}

		factory.releaseAfterCreate = append(factory.releaseAfterCreate, cowVolume)

		containerSpec.BindMounts = append(containerSpec.BindMounts, garden.BindMount{
			SrcPath: cowVolume.Path(),
			DstPath: mount.MountPath,
			Mode:    garden.BindMountModeRW,
		})

		factory.volumeHandles = append(factory.volumeHandles, cowVolume.Handle())
		factory.volumeMounts[cowVolume.Handle()] = mount.MountPath

		factory.logger.Info("created-cow-volume", lager.Data{
			"original-volume-handle": mount.Volume.Handle(),
			"cow-volume-handle":      cowVolume.Handle(),
		})
	}

	return containerSpec, nil
}
<|endoftext|>