<commit_before>package parser

import "testing"

func TestXMLParsesReturnsErrWhenNotAssignedParsableXML(t *testing.T) {
	_, err := ParseXML([]byte("<asdf>"))
	if err == nil {
		t.FailNow()
	}
}
<commit_msg>Added parser_test<commit_after>package parser

import (
	"testing"

	"github.com/stretchr/testify/require"
)

func TestXMLParsesReturnsErrWhenNotAssignedParsableXML(t *testing.T) {
	_, err := ParseXML([]byte("<asdf>"))

	require.NotNil(t, err)
}

func TestParseXMLReturnsAProperCollection(t *testing.T) {
	var correctData = []byte(`<?xml version="1.0" encoding="UTF-8"?>
    <bicing_stations>
     <updatetime><![CDATA[1415996588]]></updatetime>
      <station>
        <id>1</id>
          <type>BIKE</type>
            <lat>41.397952</lat>
              <long>2.180042</long>
                <street><![CDATA[Gran Via Corts Catalanes]]></street>
                  <height>21</height>
                    <streetNumber>760</streetNumber>
                      <nearbyStationList>24, 369, 387, 426</nearbyStationList>
                        <status>OPN</status>
                          <slots>0</slots>
                            <bikes>24</bikes>
                             </station>
                             </bicing_stations>`)

	collection, err := ParseXML(correctData)

	require.Nil(t, err)
	require.Equal(t, 1415996588, collection.Updatetime)
	require.Len(t, collection.StationStates, 1)

	stationState := collection.StationStates[0]

	require.Equal(t, 1, stationState.ID)
	require.Equal(t, 41.397952, stationState.Latitude)
	require.Equal(t, 2.180042, stationState.Longitude)
	require.Equal(t, "Gran Via Corts Catalanes", stationState.Street)
	require.Equal(t, 21, stationState.Height)
	require.Equal(t, "760", stationState.StreetNumber)
	require.Equal(t, "24, 369, 387, 426", stationState.NearbyStationList)
	require.Equal(t, "OPN", stationState.Status)
	require.Equal(t, 0, stationState.FreeSlots)
	require.Equal(t, 24, stationState.Bikes)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"fmt"
	"io"

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

// A function used by read proxies to refresh their contents. See notes on
// NewReadProxy.
type RefreshContentsFunc func(context.Context) (io.ReadCloser, error)

// Create a read proxy.
//
// The supplied function will be used to obtain the proxy's contents whenever
// the supplied file leaser decides to expire the temporary copy thus obtained.
// It must return the same contents every time, and the contents must be of the
// given size.
//
// If rl is non-nil, it will be used as the first temporary copy of the
// contents, and must match what refresh returns.
func NewReadProxy(
	fl FileLeaser,
	size int64,
	refresh RefreshContentsFunc,
	rl ReadLease) (rp *ReadProxy) {
	rp = &ReadProxy{
		leaser:  fl,
		size:    size,
		refresh: refresh,
		lease:   rl,
	}

	return
}

// A wrapper around a read lease, exposing a similar interface with the
// following differences:
//
//  *  Contents are fetched and re-fetched automatically when needed. Therefore
//     the user need not worry about lease expiration.
//
//  *  Methods that may involve fetching the contents (reading, seeking) accept
//     context arguments, so as to be cancellable.
//
// External synchronization is required.
type ReadProxy struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	size int64

	/////////////////////////
	// Dependencies
	/////////////////////////

	leaser  FileLeaser
	refresh RefreshContentsFunc

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The current wrapped lease, or nil if one has never been issued.
	lease ReadLease
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func isRevokedErr(err error) bool {
	_, ok := err.(*RevokedError)
	return ok
}

// Set up a read/write lease and fill in our contents.
//
// REQUIRES: The caller has observed that rp.lease has expired.
func (rp *ReadProxy) getContents(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// Obtain some space to write the contents.
	rwl, err = rp.leaser.NewFile()
	if err != nil {
		err = fmt.Errorf("NewFile: %v", err)
		return
	}

	// Clean up if we exit early.
	defer func() {
		if err != nil {
			rwl.Downgrade().Revoke()
		}
	}()

	// Obtain the reader for our contents.
	rc, err := rp.refresh(ctx)
	if err != nil {
		err = fmt.Errorf("User function: %v", err)
		return
	}

	defer func() {
		closeErr := rc.Close()
		if closeErr != nil && err == nil {
			err = fmt.Errorf("Close: %v", closeErr)
		}
	}()

	// Copy into the read/write lease.
	copied, err := io.Copy(rwl, rc)
	if err != nil {
		err = fmt.Errorf("Copy: %v", err)
		return
	}

	// Did the user lie about the size?
	if copied != rp.Size() {
		err = fmt.Errorf("Copied %v bytes; expected %v", copied, rp.Size())
		return
	}

	return
}

// Downgrade and save the supplied read/write lease obtained with getContents
// for later use.
func (rp *ReadProxy) saveContents(rwl ReadWriteLease) {
	rp.lease = rwl.Downgrade()
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Semantics matching io.Reader, except with context support.
func (rp *ReadProxy) Read(
	ctx context.Context,
	p []byte) (n int, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		n, err = rp.lease.Read(p)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.Read(p)

	return
}

// Semantics matching io.Seeker, except with context support.
func (rp *ReadProxy) Seek(
	ctx context.Context,
	offset int64,
	whence int) (off int64, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		off, err = rp.lease.Seek(offset, whence)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	off, err = rwl.Seek(offset, whence)

	return
}

// Semantics matching io.ReaderAt, except with context support.
func (rp *ReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		n, err = rp.lease.ReadAt(p, off)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.ReadAt(p, off)

	return
}

// Return the size of the proxied content. Guarantees to not block.
func (rp *ReadProxy) Size() (size int64) {
	size = rp.size
	return
}

// Return a read/write lease for the proxied contents, destroying the read
// proxy. The read proxy must not be used after calling this method.
func (rp *ReadProxy) Upgrade(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// If we succeed, we are now destroyed.
	defer func() {
		if err == nil {
			rp.Destroy()
		}
	}()

	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		rwl, err = rp.lease.Upgrade()
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Build the read/write lease anew.
	rwl, err = rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	return
}

// Destroy any resources in use by the read proxy. It must not be used further.
func (rp *ReadProxy) Destroy() {
	if rp.lease != nil {
		rp.lease.Revoke()
	}

	// Make use-after-destroy errors obvious.
	rp.size = 0
	rp.leaser = nil
	rp.refresh = nil
	rp.lease = nil
}
<commit_msg>Added the Refresher interface.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"fmt"
	"io"

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

// A type used by read proxies to refresh their contents. See notes on
// NewReadProxy.
type Refresher interface {
	// Return the size of the underlying contents.
	Size() (size int64)

	// Return a read-closer for the contents. The same contents will always be
	// returned, and they will always be of length Size().
	Refresh(ctx context.Context) (rc io.ReadCloser, err error)
}

// Create a read proxy.
//
// The supplied refresher will be used to obtain the proxy's contents whenever
// the file leaser decides to expire the temporary copy thus obtained.
//
// If rl is non-nil, it will be used as the first temporary copy of the
// contents, and must match what the refresher returns.
func NewReadProxy(
	fl FileLeaser,
	r Refresher,
	rl ReadLease) (rp *ReadProxy) {
	rp = &ReadProxy{
		leaser:    fl,
		refresher: r,
		lease:     rl,
	}

	return
}

// A wrapper around a read lease, exposing a similar interface with the
// following differences:
//
//  *  Contents are fetched and re-fetched automatically when needed. Therefore
//     the user need not worry about lease expiration.
//
//  *  Methods that may involve fetching the contents (reading, seeking) accept
//     context arguments, so as to be cancellable.
//
// External synchronization is required.
type ReadProxy struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	size int64

	/////////////////////////
	// Dependencies
	/////////////////////////

	leaser  FileLeaser
	refresh RefreshContentsFunc

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The current wrapped lease, or nil if one has never been issued.
	lease ReadLease
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func isRevokedErr(err error) bool {
	_, ok := err.(*RevokedError)
	return ok
}

// Set up a read/write lease and fill in our contents.
//
// REQUIRES: The caller has observed that rp.lease has expired.
func (rp *ReadProxy) getContents(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// Obtain some space to write the contents.
	rwl, err = rp.leaser.NewFile()
	if err != nil {
		err = fmt.Errorf("NewFile: %v", err)
		return
	}

	// Clean up if we exit early.
	defer func() {
		if err != nil {
			rwl.Downgrade().Revoke()
		}
	}()

	// Obtain the reader for our contents.
	rc, err := rp.refresh(ctx)
	if err != nil {
		err = fmt.Errorf("User function: %v", err)
		return
	}

	defer func() {
		closeErr := rc.Close()
		if closeErr != nil && err == nil {
			err = fmt.Errorf("Close: %v", closeErr)
		}
	}()

	// Copy into the read/write lease.
	copied, err := io.Copy(rwl, rc)
	if err != nil {
		err = fmt.Errorf("Copy: %v", err)
		return
	}

	// Did the user lie about the size?
	if copied != rp.Size() {
		err = fmt.Errorf("Copied %v bytes; expected %v", copied, rp.Size())
		return
	}

	return
}

// Downgrade and save the supplied read/write lease obtained with getContents
// for later use.
func (rp *ReadProxy) saveContents(rwl ReadWriteLease) {
	rp.lease = rwl.Downgrade()
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Semantics matching io.Reader, except with context support.
func (rp *ReadProxy) Read(
	ctx context.Context,
	p []byte) (n int, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		n, err = rp.lease.Read(p)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.Read(p)

	return
}

// Semantics matching io.Seeker, except with context support.
func (rp *ReadProxy) Seek(
	ctx context.Context,
	offset int64,
	whence int) (off int64, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		off, err = rp.lease.Seek(offset, whence)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	off, err = rwl.Seek(offset, whence)

	return
}

// Semantics matching io.ReaderAt, except with context support.
func (rp *ReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		n, err = rp.lease.ReadAt(p, off)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.ReadAt(p, off)

	return
}

// Return the size of the proxied content. Guarantees to not block.
func (rp *ReadProxy) Size() (size int64) {
	size = rp.size
	return
}

// Return a read/write lease for the proxied contents, destroying the read
// proxy. The read proxy must not be used after calling this method.
func (rp *ReadProxy) Upgrade(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// If we succeed, we are now destroyed.
	defer func() {
		if err == nil {
			rp.Destroy()
		}
	}()

	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		rwl, err = rp.lease.Upgrade()
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Build the read/write lease anew.
	rwl, err = rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	return
}

// Destroy any resources in use by the read proxy. It must not be used further.
func (rp *ReadProxy) Destroy() {
	if rp.lease != nil {
		rp.lease.Revoke()
	}

	// Make use-after-destroy errors obvious.
	rp.size = 0
	rp.leaser = nil
	rp.refresh = nil
	rp.lease = nil
}
<|endoftext|>


<commit_before>package core
<commit_msg>移除core目录<commit_after><|endoftext|>


<commit_before>// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package releaser implements a set of utilities and a wrapper around Goreleaser
// to help automate the Hugo release process.
package releaser

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"
)

const (
	issueLinkTemplate                        = "[#%d](https://github.com/gohugoio/hugo/issues/%d)"
	linkTemplate                             = "[%s](%s)"
	releaseNotesMarkdownTemplatePatchRelease = `
{{ if eq (len .All) 1 }}
This is a bug-fix release with one important fix.
{{ else }}
This is a bug-fix release with a couple of important fixes.
{{ end }}
{{ range .All }}
{{- if .GitHubCommit -}}
* {{ .Subject }} {{ . | commitURL }} {{ . | authorURL }} {{ range .Issues }}{{ . | issue }}{{ end }}
{{ else -}}
* {{ .Subject }} {{ range .Issues }}{{ . | issue }}{{ end }}
{{ end -}}
{{- end }}

`
	releaseNotesMarkdownTemplate = `
{{- $contribsPerAuthor := .All.ContribCountPerAuthor -}}
{{- $docsContribsPerAuthor := .Docs.ContribCountPerAuthor -}}

This release represents **{{ len .All }} contributions by {{ len $contribsPerAuthor }} contributors** to the main Hugo code base.

{{- if  gt (len $contribsPerAuthor) 3 -}}
{{- $u1 := index $contribsPerAuthor 0 -}}
{{- $u2 := index $contribsPerAuthor 1 -}}
{{- $u3 := index $contribsPerAuthor 2 -}}
{{- $u4 := index $contribsPerAuthor 3 -}}
{{- $u1.AuthorLink }} leads the Hugo development with a significant amount of contributions, but also a big shoutout to {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their ongoing contributions.
And a big thanks to [@digitalcraftsman](https://github.com/digitalcraftsman) for his relentless work on keeping the themes site in pristine condition and to [@davidsneighbour](https://github.com/davidsneighbour), [@coliff](https://github.com/coliff) and [@kaushalmodi](https://github.com/kaushalmodi) for all the great work on the documentation site.
{{ end }}
Many have also been busy writing and fixing the documentation in [hugoDocs](https://github.com/gohugoio/hugoDocs),
which has received **{{ len .Docs }} contributions by {{ len $docsContribsPerAuthor }} contributors**.
{{- if  gt (len $docsContribsPerAuthor) 3 -}}
{{- $u1 := index $docsContribsPerAuthor 0 -}}
{{- $u2 := index $docsContribsPerAuthor 1 -}}
{{- $u3 := index $docsContribsPerAuthor 2 -}}
{{- $u4 := index $docsContribsPerAuthor 3 }} A special thanks to {{ $u1.AuthorLink }}, {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their work on the documentation site.
{{ end }}

Hugo now has:

{{ with .Repo -}}
* {{ .Stars }}+ [stars](https://github.com/gohugoio/hugo/stargazers)
* {{ len .Contributors }}+ [contributors](https://github.com/gohugoio/hugo/graphs/contributors)
{{- end -}}
{{ with .ThemeCount }}
* {{ . }}+ [themes](http://themes.gohugo.io/)
{{ end }}
{{ with .Notes }}
## Notes
{{ template "change-section" . }}
{{- end -}}
## Enhancements
{{ template "change-headers"  .Enhancements -}}
## Fixes
{{ template "change-headers"  .Fixes -}}

{{ define "change-headers" }}
{{ $tmplChanges := index . "templateChanges" -}}
{{- $outChanges := index . "outChanges" -}}
{{- $coreChanges := index . "coreChanges" -}}
{{- $otherChanges := index . "otherChanges" -}}
{{- with $tmplChanges -}}
### Templates
{{ template "change-section" . }}
{{- end -}}
{{- with $outChanges -}}
### Output
{{ template "change-section"  . }}
{{- end -}}
{{- with $coreChanges -}}
### Core
{{ template "change-section" . }}
{{- end -}}
{{- with $otherChanges -}}
### Other
{{ template "change-section"  . }}
{{- end -}}
{{ end }}

{{ define "change-section" }}
{{ range . }}
{{- if .GitHubCommit -}}
* {{ .Subject }} {{ . | commitURL }} {{ . | authorURL }} {{ range .Issues }}{{ . | issue }}{{ end }}
{{ else -}}
* {{ .Subject }} {{ range .Issues }}{{ . | issue }}{{ end }}
{{ end -}}
{{- end }}
{{ end }}
`
)

var templateFuncs = template.FuncMap{
	"isPatch": func(c changeLog) bool {
		return !strings.HasSuffix(c.Version, "0")
	},
	"issue": func(id int) string {
		return fmt.Sprintf(issueLinkTemplate, id, id)
	},
	"commitURL": func(info gitInfo) string {
		if info.GitHubCommit.HTMLURL == "" {
			return ""
		}
		return fmt.Sprintf(linkTemplate, info.Hash, info.GitHubCommit.HTMLURL)
	},
	"authorURL": func(info gitInfo) string {
		if info.GitHubCommit.Author.Login == "" {
			return ""
		}
		return fmt.Sprintf(linkTemplate, "@"+info.GitHubCommit.Author.Login, info.GitHubCommit.Author.HTMLURL)
	},
}

func writeReleaseNotes(version string, infosMain, infosDocs gitInfos, to io.Writer) error {
	client := newGitHubAPI("hugo")
	changes := gitInfosToChangeLog(infosMain, infosDocs)
	changes.Version = version
	repo, err := client.fetchRepo()
	if err == nil {
		changes.Repo = &repo
	}
	themeCount, err := fetchThemeCount()
	if err == nil {
		changes.ThemeCount = themeCount
	}

	mtempl := releaseNotesMarkdownTemplate

	if !strings.HasSuffix(version, "0") {
		mtempl = releaseNotesMarkdownTemplatePatchRelease
	}

	tmpl, err := template.New("").Funcs(templateFuncs).Parse(mtempl)
	if err != nil {
		return err
	}

	err = tmpl.Execute(to, changes)
	if err != nil {
		return err
	}

	return nil
}

func fetchThemeCount() (int, error) {
	resp, err := http.Get("https://raw.githubusercontent.com/gohugoio/hugoThemes/master/.gitmodules")
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	b, _ := ioutil.ReadAll(resp.Body)
	return bytes.Count(b, []byte("submodule")), nil
}

func writeReleaseNotesToTmpFile(version string, infosMain, infosDocs gitInfos) (string, error) {
	f, err := ioutil.TempFile("", "hugorelease")
	if err != nil {
		return "", err
	}

	defer f.Close()

	if err := writeReleaseNotes(version, infosMain, infosDocs, f); err != nil {
		return "", err
	}

	return f.Name(), nil
}

func getReleaseNotesDocsTempDirAndName(version string, final bool) (string, string) {
	if final {
		return hugoFilepath("temp"), fmt.Sprintf("%s-relnotes-ready.md", version)
	}
	return hugoFilepath("temp"), fmt.Sprintf("%s-relnotes.md", version)
}

func getReleaseNotesDocsTempFilename(version string, final bool) string {
	return filepath.Join(getReleaseNotesDocsTempDirAndName(version, final))
}

func (r *ReleaseHandler) releaseNotesState(version string) (releaseNotesState, error) {
	docsTempPath, name := getReleaseNotesDocsTempDirAndName(version, false)
	_, err := os.Stat(filepath.Join(docsTempPath, name))

	if err == nil {
		return releaseNotesCreated, nil
	}

	docsTempPath, name = getReleaseNotesDocsTempDirAndName(version, true)
	_, err = os.Stat(filepath.Join(docsTempPath, name))

	if err == nil {
		return releaseNotesReady, nil
	}

	if !os.IsNotExist(err) {
		return releaseNotesNone, err
	}

	return releaseNotesNone, nil
}

func (r *ReleaseHandler) writeReleaseNotesToTemp(version string, isPatch bool, infosMain, infosDocs gitInfos) (string, error) {
	docsTempPath, name := getReleaseNotesDocsTempDirAndName(version, isPatch)

	var w io.WriteCloser

	if !r.try {
		os.Mkdir(docsTempPath, os.ModePerm)

		f, err := os.Create(filepath.Join(docsTempPath, name))
		if err != nil {
			return "", err
		}

		name = f.Name()

		defer f.Close()

		w = f

	} else {
		w = os.Stdout
	}

	if err := writeReleaseNotes(version, infosMain, infosDocs, w); err != nil {
		return "", err
	}

	return name, nil
}

func (r *ReleaseHandler) writeReleaseNotesToDocs(title, description, sourceFilename string) (string, error) {
	targetFilename := "index.md"
	bundleDir := strings.TrimSuffix(filepath.Base(sourceFilename), "-ready.md")
	contentDir := hugoFilepath("docs/content/en/news/" + bundleDir)
	targetFullFilename := filepath.Join(contentDir, targetFilename)

	if r.try {
		fmt.Printf("Write release notes to /docs: Bundle %q Dir: %q\n", bundleDir, contentDir)
		return targetFullFilename, nil
	}

	if err := os.MkdirAll(contentDir, os.ModePerm); err != nil {
		return "", nil
	}

	b, err := ioutil.ReadFile(sourceFilename)
	if err != nil {
		return "", err
	}

	f, err := os.Create(targetFullFilename)
	if err != nil {
		return "", err
	}
	defer f.Close()

	fmTail := ""
	if !strings.HasSuffix(title, ".0") {
		// Bug fix release
		fmTail = `
images:
- images/blog/hugo-bug-poster.png
`
	}

	if _, err := f.WriteString(fmt.Sprintf(`
---
date: %s
title: %q
description: %q
categories: ["Releases"]%s
---

	`, time.Now().Format("2006-01-02"), title, description, fmTail)); err != nil {
		return "", err
	}

	if _, err := f.Write(b); err != nil {
		return "", err
	}

	return targetFullFilename, nil
}
<commit_msg>Remove duplicate references from release notes<commit_after>// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package releaser implements a set of utilities and a wrapper around Goreleaser
// to help automate the Hugo release process.
package releaser

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"
)

const (
	issueLinkTemplate                        = "[#%d](https://github.com/gohugoio/hugo/issues/%d)"
	linkTemplate                             = "[%s](%s)"
	releaseNotesMarkdownTemplatePatchRelease = `
{{ if eq (len .All) 1 }}
This is a bug-fix release with one important fix.
{{ else }}
This is a bug-fix release with a couple of important fixes.
{{ end }}
{{ range .All }}
{{- if .GitHubCommit -}}
* {{ .Subject }} {{ . | commitURL }} {{ . | authorURL }} {{ range .Issues }}{{ . | issue }}{{ end }}
{{ else -}}
* {{ .Subject }} {{ range .Issues }}{{ . | issue }}{{ end }}
{{ end -}}
{{- end }}

`
	releaseNotesMarkdownTemplate = `
{{- $contribsPerAuthor := .All.ContribCountPerAuthor -}}
{{- $docsContribsPerAuthor := .Docs.ContribCountPerAuthor -}}

This release represents **{{ len .All }} contributions by {{ len $contribsPerAuthor }} contributors** to the main Hugo code base.

{{- if  gt (len $contribsPerAuthor) 3 -}}
{{- $u1 := index $contribsPerAuthor 0 -}}
{{- $u2 := index $contribsPerAuthor 1 -}}
{{- $u3 := index $contribsPerAuthor 2 -}}
{{- $u4 := index $contribsPerAuthor 3 -}}
{{- $u1.AuthorLink }} leads the Hugo development with a significant amount of contributions, but also a big shoutout to {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their ongoing contributions.
And a big thanks to [@digitalcraftsman](https://github.com/digitalcraftsman) for his relentless work on keeping the themes site in pristine condition.
{{ end }}
Many have also been busy writing and fixing the documentation in [hugoDocs](https://github.com/gohugoio/hugoDocs),
which has received **{{ len .Docs }} contributions by {{ len $docsContribsPerAuthor }} contributors**.
{{- if  gt (len $docsContribsPerAuthor) 3 -}}
{{- $u1 := index $docsContribsPerAuthor 0 -}}
{{- $u2 := index $docsContribsPerAuthor 1 -}}
{{- $u3 := index $docsContribsPerAuthor 2 -}}
{{- $u4 := index $docsContribsPerAuthor 3 }} A special thanks to {{ $u1.AuthorLink }}, {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their work on the documentation site.
{{ end }}

Hugo now has:

{{ with .Repo -}}
* {{ .Stars }}+ [stars](https://github.com/gohugoio/hugo/stargazers)
* {{ len .Contributors }}+ [contributors](https://github.com/gohugoio/hugo/graphs/contributors)
{{- end -}}
{{ with .ThemeCount }}
* {{ . }}+ [themes](http://themes.gohugo.io/)
{{ end }}
{{ with .Notes }}
## Notes
{{ template "change-section" . }}
{{- end -}}
## Enhancements
{{ template "change-headers"  .Enhancements -}}
## Fixes
{{ template "change-headers"  .Fixes -}}

{{ define "change-headers" }}
{{ $tmplChanges := index . "templateChanges" -}}
{{- $outChanges := index . "outChanges" -}}
{{- $coreChanges := index . "coreChanges" -}}
{{- $otherChanges := index . "otherChanges" -}}
{{- with $tmplChanges -}}
### Templates
{{ template "change-section" . }}
{{- end -}}
{{- with $outChanges -}}
### Output
{{ template "change-section"  . }}
{{- end -}}
{{- with $coreChanges -}}
### Core
{{ template "change-section" . }}
{{- end -}}
{{- with $otherChanges -}}
### Other
{{ template "change-section"  . }}
{{- end -}}
{{ end }}

{{ define "change-section" }}
{{ range . }}
{{- if .GitHubCommit -}}
* {{ .Subject }} {{ . | commitURL }} {{ . | authorURL }} {{ range .Issues }}{{ . | issue }}{{ end }}
{{ else -}}
* {{ .Subject }} {{ range .Issues }}{{ . | issue }}{{ end }}
{{ end -}}
{{- end }}
{{ end }}
`
)

var templateFuncs = template.FuncMap{
	"isPatch": func(c changeLog) bool {
		return !strings.HasSuffix(c.Version, "0")
	},
	"issue": func(id int) string {
		return fmt.Sprintf(issueLinkTemplate, id, id)
	},
	"commitURL": func(info gitInfo) string {
		if info.GitHubCommit.HTMLURL == "" {
			return ""
		}
		return fmt.Sprintf(linkTemplate, info.Hash, info.GitHubCommit.HTMLURL)
	},
	"authorURL": func(info gitInfo) string {
		if info.GitHubCommit.Author.Login == "" {
			return ""
		}
		return fmt.Sprintf(linkTemplate, "@"+info.GitHubCommit.Author.Login, info.GitHubCommit.Author.HTMLURL)
	},
}

func writeReleaseNotes(version string, infosMain, infosDocs gitInfos, to io.Writer) error {
	client := newGitHubAPI("hugo")
	changes := gitInfosToChangeLog(infosMain, infosDocs)
	changes.Version = version
	repo, err := client.fetchRepo()
	if err == nil {
		changes.Repo = &repo
	}
	themeCount, err := fetchThemeCount()
	if err == nil {
		changes.ThemeCount = themeCount
	}

	mtempl := releaseNotesMarkdownTemplate

	if !strings.HasSuffix(version, "0") {
		mtempl = releaseNotesMarkdownTemplatePatchRelease
	}

	tmpl, err := template.New("").Funcs(templateFuncs).Parse(mtempl)
	if err != nil {
		return err
	}

	err = tmpl.Execute(to, changes)
	if err != nil {
		return err
	}

	return nil
}

func fetchThemeCount() (int, error) {
	resp, err := http.Get("https://raw.githubusercontent.com/gohugoio/hugoThemes/master/.gitmodules")
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	b, _ := ioutil.ReadAll(resp.Body)
	return bytes.Count(b, []byte("submodule")), nil
}

func writeReleaseNotesToTmpFile(version string, infosMain, infosDocs gitInfos) (string, error) {
	f, err := ioutil.TempFile("", "hugorelease")
	if err != nil {
		return "", err
	}

	defer f.Close()

	if err := writeReleaseNotes(version, infosMain, infosDocs, f); err != nil {
		return "", err
	}

	return f.Name(), nil
}

func getReleaseNotesDocsTempDirAndName(version string, final bool) (string, string) {
	if final {
		return hugoFilepath("temp"), fmt.Sprintf("%s-relnotes-ready.md", version)
	}
	return hugoFilepath("temp"), fmt.Sprintf("%s-relnotes.md", version)
}

func getReleaseNotesDocsTempFilename(version string, final bool) string {
	return filepath.Join(getReleaseNotesDocsTempDirAndName(version, final))
}

func (r *ReleaseHandler) releaseNotesState(version string) (releaseNotesState, error) {
	docsTempPath, name := getReleaseNotesDocsTempDirAndName(version, false)
	_, err := os.Stat(filepath.Join(docsTempPath, name))

	if err == nil {
		return releaseNotesCreated, nil
	}

	docsTempPath, name = getReleaseNotesDocsTempDirAndName(version, true)
	_, err = os.Stat(filepath.Join(docsTempPath, name))

	if err == nil {
		return releaseNotesReady, nil
	}

	if !os.IsNotExist(err) {
		return releaseNotesNone, err
	}

	return releaseNotesNone, nil
}

func (r *ReleaseHandler) writeReleaseNotesToTemp(version string, isPatch bool, infosMain, infosDocs gitInfos) (string, error) {
	docsTempPath, name := getReleaseNotesDocsTempDirAndName(version, isPatch)

	var w io.WriteCloser

	if !r.try {
		os.Mkdir(docsTempPath, os.ModePerm)

		f, err := os.Create(filepath.Join(docsTempPath, name))
		if err != nil {
			return "", err
		}

		name = f.Name()

		defer f.Close()

		w = f

	} else {
		w = os.Stdout
	}

	if err := writeReleaseNotes(version, infosMain, infosDocs, w); err != nil {
		return "", err
	}

	return name, nil
}

func (r *ReleaseHandler) writeReleaseNotesToDocs(title, description, sourceFilename string) (string, error) {
	targetFilename := "index.md"
	bundleDir := strings.TrimSuffix(filepath.Base(sourceFilename), "-ready.md")
	contentDir := hugoFilepath("docs/content/en/news/" + bundleDir)
	targetFullFilename := filepath.Join(contentDir, targetFilename)

	if r.try {
		fmt.Printf("Write release notes to /docs: Bundle %q Dir: %q\n", bundleDir, contentDir)
		return targetFullFilename, nil
	}

	if err := os.MkdirAll(contentDir, os.ModePerm); err != nil {
		return "", nil
	}

	b, err := ioutil.ReadFile(sourceFilename)
	if err != nil {
		return "", err
	}

	f, err := os.Create(targetFullFilename)
	if err != nil {
		return "", err
	}
	defer f.Close()

	fmTail := ""
	if !strings.HasSuffix(title, ".0") {
		// Bug fix release
		fmTail = `
images:
- images/blog/hugo-bug-poster.png
`
	}

	if _, err := f.WriteString(fmt.Sprintf(`
---
date: %s
title: %q
description: %q
categories: ["Releases"]%s
---

	`, time.Now().Format("2006-01-02"), title, description, fmTail)); err != nil {
		return "", err
	}

	if _, err := f.Write(b); err != nil {
		return "", err
	}

	return targetFullFilename, nil
}
<|endoftext|>


<commit_before>package ogdatv21

import (
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"github.com/the42/ogdat"
	"net/url"
	"strconv"
	"time"
)

const Version21 = "OGD Austria Metadata 2.1" // Version 2.1: 15.10.2012
const specfile = "ogdat_spec-2.1.csv"
const TimeSpecifier = "2006-01-02T15:04:05" // RFC 3339 = ISO 8601 ohne Zeitzone

/// BEGIN:check wheater this code may be factored out
const (
	Time2 = time.RFC3339Nano
	Time3 = time.RFC3339
	Time1 = TimeSpecifier
	TimeFormatUnknow
)

type Kategorie struct {
	NumID       int `json:"-"`
	ID          string
	PrettyName  string `json:"-"`
	RDFProperty string `json:"-"`
}

func (kat *Kategorie) String() string {
	return kat.PrettyName
}

var (
	Arbeit           = Kategorie{NumID: 1, ID: "arbeit", PrettyName: "Arbeit", RDFProperty: ""}
	Bevoelkerung     = Kategorie{NumID: 2, ID: "bevölkerung", PrettyName: "Bevölkerung", RDFProperty: ""}
	BildungForschung = Kategorie{NumID: 3, ID: "bildung-und-forschung", PrettyName: "Bildung und Forschung", RDFProperty: ""}
	FinanzRW         = Kategorie{NumID: 4, ID: "finanzen-und-rechnungswesen", PrettyName: "Finanzen und Rechnungswesen", RDFProperty: ""}
	GeographPlanung  = Kategorie{NumID: 5, ID: "geographie-und-planung", PrettyName: "Geographie und Planung", RDFProperty: ""}
	GesellSoziales   = Kategorie{NumID: 6, ID: "gesellschaft-und-soziales", PrettyName: "Gesellschaft und Soziales", RDFProperty: ""}
	Gesundheit       = Kategorie{NumID: 7, ID: "gesundheit", PrettyName: "Gesundheit", RDFProperty: ""}
	KunstKultur      = Kategorie{NumID: 8, ID: "kunst-und-kultur", PrettyName: "Kunst und Kultur", RDFProperty: ""}
	LandFW           = Kategorie{NumID: 9, ID: "land-und-forstwirtschaft", PrettyName: "Land und Forstwirtschaft", RDFProperty: ""}
	SportFZ          = Kategorie{NumID: 10, ID: "sport-und-freizeit", PrettyName: "Sport und Freizeit", RDFProperty: ""}
	Umwelt           = Kategorie{NumID: 11, ID: "umwelt", PrettyName: "Umwelt", RDFProperty: ""}
	VerkehrTechnik   = Kategorie{NumID: 12, ID: "verkehr-und-technik", PrettyName: "Verkehr und Technik", RDFProperty: ""}
	VerwaltPol       = Kategorie{NumID: 13, ID: "verwaltung-und-politik", PrettyName: "Verwaltung und Politik", RDFProperty: ""}
	WirtTourism      = Kategorie{NumID: 14, ID: "wirtschaft-und-tourismus", PrettyName: "Wirtschaft und Tourismus", RDFProperty: ""}
)

var categories = []Kategorie{
	Arbeit,
	Bevoelkerung,
	BildungForschung,
	FinanzRW,
	GeographPlanung,
	GesellSoziales,
	Gesundheit,
	KunstKultur,
	LandFW,
	SportFZ,
	Umwelt,
	VerkehrTechnik,
	VerwaltPol,
	WirtTourism,
}

var categorymap = make(map[string]Kategorie)

type Tags string
type ResourceSpecifier string

type Cycle struct {
	NumID                       int
	DomainCode                  string
	MD_MaintenanceFrequencyCode string
	Name_DE                     string
}

var (
	CycCont     = Cycle{1, "001", "continual", "kontinuierlich"}
	CycDaily    = Cycle{2, "002", "daily", "täglich"}
	CycWeekly   = Cycle{3, "003", "weekly", "wöchentlich"}
	CycFortNly  = Cycle{4, "004", "fortnightly", "14-tägig"}
	CycMonthly  = Cycle{5, "005", "monthly", "monatlich"}
	CycQuart    = Cycle{6, "006", "quarterly", "quartalsweise"}
	CycBiAnn    = Cycle{7, "007", "biannually", "halbjährlich"}
	CycAnnually = Cycle{8, "008", "annually", "jährlich"}
	CycNeeded   = Cycle{9, "009", "asNeeded", "nach Bedarf"}
	CycIrreg    = Cycle{10, "010", "irregular", "unregelmäßig"}
	CycNP       = Cycle{11, "011", "notPlanned", "nicht geplant"}
	CycUnknown  = Cycle{12, "012", "unknown", "unbekannt"}
)

var cycles = []Cycle{
	CycCont,
	CycDaily,
	CycWeekly,
	CycFortNly,
	CycMonthly,
	CycQuart,
	CycBiAnn,
	CycAnnually,
	CycNeeded,
	CycIrreg,
	CycNP,
	CycUnknown,
}

type Url struct {
	*url.URL
	Raw string
}

type Identfier struct {
	*uuid.UUID
	Raw string
}

func (id *Identfier) String() string {
	return id.Raw
}

type Time struct {
	*time.Time
	Raw    string
	Format string
}

func (time *Time) String() string {
	return time.Raw
}

func (cyc *Cycle) String() string {
	return cyc.Name_DE
}

func cmpstrtocycle(raw string, cyc Cycle) bool {
	if raw == cyc.Name_DE || raw == cyc.DomainCode || raw == cyc.MD_MaintenanceFrequencyCode {
		return true
	}
	if len(raw) > 0 {
		if i, err := strconv.Atoi(raw); err == nil && i == cyc.NumID {
			return true
		}
	}
	return false
}

func (cyc *Cycle) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	var found bool
	var idx int
	var matchcyc Cycle

	for idx, matchcyc = range cycles {
		if found := cmpstrtocycle(raw, matchcyc); found == true {
			break
		}
	}

	if found {
		*cyc = cycles[idx]
	} else {
		cyc.NumID = -1
		cyc.Name_DE = "**** NON cycle spec **** - " + raw
		cyc.MD_MaintenanceFrequencyCode = cyc.Name_DE
	}
	return nil
}

func (ogdtime *Time) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}
	ogdtime.Raw = raw

	ogdtime.Format = Time1
	t, err := time.Parse(ogdtime.Format, raw)
	if err != nil {
		ogdtime.Format = Time2
		t, err = time.Parse(ogdtime.Format, raw)
		if err != nil {
			ogdtime.Format = Time3
			t, err = time.Parse(ogdtime.Format, raw)
			if err != nil {
				ogdtime.Format = TimeFormatUnknow
			}
		}
	}
	ogdtime.Time = &t
	return nil
}

func (u *Url) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}
	u.Raw = raw
	url, _ := url.Parse(raw) // an actuall error is not important. If url can not be parsed, result will be nil, which is fine here
	u.URL = url
	return nil
}

func (id *Identfier) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	id.Raw = string(raw)
	if uuid := uuid.Parse(raw); uuid != nil {
		id.UUID = &uuid
	}
	return nil
}

func (kat *Kategorie) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	corecat, found := categorymap[raw]
	if !found {
		kat.NumID = -1
		kat.ID = raw
		kat.PrettyName = "**** NON core category **** - " + kat.ID
	} else {
		*kat = corecat
	}
	return nil
}

/// END:check wheater this code may be factored out

type Extras struct {
	// Core
	Metadata_Identifier Identfier   `json:"metadata_identifier"` // CKAN uses since API Version 2 a UUID V4, cf. https://github.com/okfn/ckan/blob/master/ckan/model/types.py
	Metadata_Modified   *Time       `json:"metadata_modified"`
	Categorization      []Kategorie `json:"categorization"`
	Begin_DateTime      *Time       `json:"begin_datetime"`

	// Optional
	Schema_Name           *string `json:"schema_name"`
	Schema_Language       *string `json:"schema_language"`     // always "ger"
	Schema_Characterset   *string `json:"schema_characterset"` // always "utf8", cf. https://www.ghrsst.org/files/download.php?m=documents&f=ISO%2019115%20.pdf
	MetaData_Linkage      []Url   `json:"metadata_linkage"`
	Attribute_Description *string `json:"attribute_description"`
	Maintainer_Link       *Url    `json:"maintainer_link"`
	Publisher             *string `json:"publisher"`
	Geographich_Toponym   *string `json:"geographic_toponym"`

	/*  ON/EN/ISO 19115:2003: westBL (344) & eastBL (345) & southBL (346) & northBL (347)
	 * TODO: Specifiaction says a WKT of POLYGON should be used, which would make a
	 * POLYGON ((-180.00 -90.00, 180.00 90.00)) but Example states
	 * POLYGON (-180.00 -90.00, 180.00 90.00)
	 * UNDER CLARIFICATION
	 */
	Geographic_BBox  *string `json:"geographic_bbox"`
	End_DateTime     *Time   `json:"end_datetime"`
	Update_Frequency *Cycle  `json:"update_frequency"`
	Lineage_Quality  *string `json:"lineage_quality"`
	EnTitleDesc      *string `json:"en_title_and_desc"`
}

type Resource struct {
	// Core
	URL    *Url              `json:"url"`
	Format ResourceSpecifier `json:"format"`

	// Optional
	Name         *string `json:"name"`
	Created      *Time   `json:"created"`
	LastModified *Time   `json:"last_modified"`

	/*
	 * dcat:bytes a rdf:Property, owl:DatatypeProperty;
	 * rdfs:isDefinedBy <http://www.w3.org/ns/dcat>;
	 * rdfs:label "size in bytes";
	 * rdfs:comment "describe size of resource in bytes";
	 * rdfs:domain dcat:Distribution;
	 * rdfs:range xsd:integer .
	 */
	Size             *string `json:"size"`
	License_Citation *string `json:"license_citation"`
	Language         *string `json:"language"`
	/* Here we have a problem in spec 2.1. which says "nach ISO\IEC 10646-1", which means utf-8, utf-16 and utf-32.
	 * We would certainly support more encodings, as eg.
	 * ISO 19115 / B.5.10 MD_CharacterSetCode<> or
	 * http://www.iana.org/assignments/character-sets/character-sets.xml
	 */
	Encoding *string `json:"characterset"`
}

type MetaData struct {
	// Core
	Title       string `json:"title"`
	Description string `json:"notes"`
	Schlagworte []Tags `json:"tags"`
	Maintainer  string `json:"maintainer"`
	License     string `json:"license"` // Sollte URI des Lizenzdokuments sein

	// nested structs
	Extras   `json:"extras"`
	Resource []Resource `json:"resources"`
}

func init() {
	for idx, val := range categories {
		categorymap[val.ID] = categories[idx]
	}
	ogdat.Register(Version21, specfile)
}
<commit_msg>go fix suggested the fix so do it<commit_after>package ogdatv21

import (
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"github.com/the42/ogdat"
	"net/url"
	"strconv"
	"time"
)

const Version21 = "OGD Austria Metadata 2.1" // Version 2.1: 15.10.2012
const specfile = "ogdat_spec-2.1.csv"
const TimeSpecifier = "2006-01-02T15:04:05" // RFC 3339 = ISO 8601 ohne Zeitzone

/// BEGIN:check wheater this code may be factored out
const (
	Time2 = time.RFC3339Nano
	Time3 = time.RFC3339
	Time1 = TimeSpecifier
	TimeFormatUnknow
)

type Kategorie struct {
	NumID       int `json:"-"`
	ID          string
	PrettyName  string `json:"-"`
	RDFProperty string `json:"-"`
}

func (kat *Kategorie) String() string {
	return kat.PrettyName
}

var (
	Arbeit           = Kategorie{NumID: 1, ID: "arbeit", PrettyName: "Arbeit", RDFProperty: ""}
	Bevoelkerung     = Kategorie{NumID: 2, ID: "bevölkerung", PrettyName: "Bevölkerung", RDFProperty: ""}
	BildungForschung = Kategorie{NumID: 3, ID: "bildung-und-forschung", PrettyName: "Bildung und Forschung", RDFProperty: ""}
	FinanzRW         = Kategorie{NumID: 4, ID: "finanzen-und-rechnungswesen", PrettyName: "Finanzen und Rechnungswesen", RDFProperty: ""}
	GeographPlanung  = Kategorie{NumID: 5, ID: "geographie-und-planung", PrettyName: "Geographie und Planung", RDFProperty: ""}
	GesellSoziales   = Kategorie{NumID: 6, ID: "gesellschaft-und-soziales", PrettyName: "Gesellschaft und Soziales", RDFProperty: ""}
	Gesundheit       = Kategorie{NumID: 7, ID: "gesundheit", PrettyName: "Gesundheit", RDFProperty: ""}
	KunstKultur      = Kategorie{NumID: 8, ID: "kunst-und-kultur", PrettyName: "Kunst und Kultur", RDFProperty: ""}
	LandFW           = Kategorie{NumID: 9, ID: "land-und-forstwirtschaft", PrettyName: "Land und Forstwirtschaft", RDFProperty: ""}
	SportFZ          = Kategorie{NumID: 10, ID: "sport-und-freizeit", PrettyName: "Sport und Freizeit", RDFProperty: ""}
	Umwelt           = Kategorie{NumID: 11, ID: "umwelt", PrettyName: "Umwelt", RDFProperty: ""}
	VerkehrTechnik   = Kategorie{NumID: 12, ID: "verkehr-und-technik", PrettyName: "Verkehr und Technik", RDFProperty: ""}
	VerwaltPol       = Kategorie{NumID: 13, ID: "verwaltung-und-politik", PrettyName: "Verwaltung und Politik", RDFProperty: ""}
	WirtTourism      = Kategorie{NumID: 14, ID: "wirtschaft-und-tourismus", PrettyName: "Wirtschaft und Tourismus", RDFProperty: ""}
)

var categories = []Kategorie{
	Arbeit,
	Bevoelkerung,
	BildungForschung,
	FinanzRW,
	GeographPlanung,
	GesellSoziales,
	Gesundheit,
	KunstKultur,
	LandFW,
	SportFZ,
	Umwelt,
	VerkehrTechnik,
	VerwaltPol,
	WirtTourism,
}

var categorymap = make(map[string]Kategorie)

type Tags string
type ResourceSpecifier string

type Cycle struct {
	NumID                       int
	DomainCode                  string
	MD_MaintenanceFrequencyCode string
	Name_DE                     string
}

var (
	CycCont     = Cycle{1, "001", "continual", "kontinuierlich"}
	CycDaily    = Cycle{2, "002", "daily", "täglich"}
	CycWeekly   = Cycle{3, "003", "weekly", "wöchentlich"}
	CycFortNly  = Cycle{4, "004", "fortnightly", "14-tägig"}
	CycMonthly  = Cycle{5, "005", "monthly", "monatlich"}
	CycQuart    = Cycle{6, "006", "quarterly", "quartalsweise"}
	CycBiAnn    = Cycle{7, "007", "biannually", "halbjährlich"}
	CycAnnually = Cycle{8, "008", "annually", "jährlich"}
	CycNeeded   = Cycle{9, "009", "asNeeded", "nach Bedarf"}
	CycIrreg    = Cycle{10, "010", "irregular", "unregelmäßig"}
	CycNP       = Cycle{11, "011", "notPlanned", "nicht geplant"}
	CycUnknown  = Cycle{12, "012", "unknown", "unbekannt"}
)

var cycles = []Cycle{
	CycCont,
	CycDaily,
	CycWeekly,
	CycFortNly,
	CycMonthly,
	CycQuart,
	CycBiAnn,
	CycAnnually,
	CycNeeded,
	CycIrreg,
	CycNP,
	CycUnknown,
}

type Url struct {
	*url.URL
	Raw string
}

type Identfier struct {
	*uuid.UUID
	Raw string
}

func (id *Identfier) String() string {
	return id.Raw
}

type Time struct {
	time.Time
	Raw    string
	Format string
}

func (time *Time) String() string {
	return time.Raw
}

func (cyc *Cycle) String() string {
	return cyc.Name_DE
}

func cmpstrtocycle(raw string, cyc Cycle) bool {
	if raw == cyc.Name_DE || raw == cyc.DomainCode || raw == cyc.MD_MaintenanceFrequencyCode {
		return true
	}
	if len(raw) > 0 {
		if i, err := strconv.Atoi(raw); err == nil && i == cyc.NumID {
			return true
		}
	}
	return false
}

func (cyc *Cycle) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	var found bool
	var idx int
	var matchcyc Cycle

	for idx, matchcyc = range cycles {
		if found := cmpstrtocycle(raw, matchcyc); found == true {
			break
		}
	}

	if found {
		*cyc = cycles[idx]
	} else {
		cyc.NumID = -1
		cyc.Name_DE = "**** NON cycle spec **** - " + raw
		cyc.MD_MaintenanceFrequencyCode = cyc.Name_DE
	}
	return nil
}

func (ogdtime *Time) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}
	ogdtime.Raw = raw

	ogdtime.Format = Time1
	t, err := time.Parse(ogdtime.Format, raw)
	if err != nil {
		ogdtime.Format = Time2
		t, err = time.Parse(ogdtime.Format, raw)
		if err != nil {
			ogdtime.Format = Time3
			t, err = time.Parse(ogdtime.Format, raw)
			if err != nil {
				ogdtime.Format = TimeFormatUnknow
			}
		}
	}
	ogdtime.Time = t
	return nil
}

func (u *Url) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}
	u.Raw = raw
	url, _ := url.Parse(raw) // an actuall error is not important. If url can not be parsed, result will be nil, which is fine here
	u.URL = url
	return nil
}

func (id *Identfier) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	id.Raw = string(raw)
	if uuid := uuid.Parse(raw); uuid != nil {
		id.UUID = &uuid
	}
	return nil
}

func (kat *Kategorie) UnmarshalJSON(data []byte) error {
	var raw string
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	corecat, found := categorymap[raw]
	if !found {
		kat.NumID = -1
		kat.ID = raw
		kat.PrettyName = "**** NON core category **** - " + kat.ID
	} else {
		*kat = corecat
	}
	return nil
}

/// END:check wheater this code may be factored out

type Extras struct {
	// Core
	Metadata_Identifier Identfier   `json:"metadata_identifier"` // CKAN uses since API Version 2 a UUID V4, cf. https://github.com/okfn/ckan/blob/master/ckan/model/types.py
	Metadata_Modified   *Time       `json:"metadata_modified"`
	Categorization      []Kategorie `json:"categorization"`
	Begin_DateTime      *Time       `json:"begin_datetime"`

	// Optional
	Schema_Name           *string `json:"schema_name"`
	Schema_Language       *string `json:"schema_language"`     // always "ger"
	Schema_Characterset   *string `json:"schema_characterset"` // always "utf8", cf. https://www.ghrsst.org/files/download.php?m=documents&f=ISO%2019115%20.pdf
	MetaData_Linkage      []Url   `json:"metadata_linkage"`
	Attribute_Description *string `json:"attribute_description"`
	Maintainer_Link       *Url    `json:"maintainer_link"`
	Publisher             *string `json:"publisher"`
	Geographich_Toponym   *string `json:"geographic_toponym"`

	/*  ON/EN/ISO 19115:2003: westBL (344) & eastBL (345) & southBL (346) & northBL (347)
	 * TODO: Specifiaction says a WKT of POLYGON should be used, which would make a
	 * POLYGON ((-180.00 -90.00, 180.00 90.00)) but Example states
	 * POLYGON (-180.00 -90.00, 180.00 90.00)
	 * UNDER CLARIFICATION
	 */
	Geographic_BBox  *string `json:"geographic_bbox"`
	End_DateTime     *Time   `json:"end_datetime"`
	Update_Frequency *Cycle  `json:"update_frequency"`
	Lineage_Quality  *string `json:"lineage_quality"`
	EnTitleDesc      *string `json:"en_title_and_desc"`
}

type Resource struct {
	// Core
	URL    *Url              `json:"url"`
	Format ResourceSpecifier `json:"format"`

	// Optional
	Name         *string `json:"name"`
	Created      *Time   `json:"created"`
	LastModified *Time   `json:"last_modified"`

	/*
	 * dcat:bytes a rdf:Property, owl:DatatypeProperty;
	 * rdfs:isDefinedBy <http://www.w3.org/ns/dcat>;
	 * rdfs:label "size in bytes";
	 * rdfs:comment "describe size of resource in bytes";
	 * rdfs:domain dcat:Distribution;
	 * rdfs:range xsd:integer .
	 */
	Size             *string `json:"size"`
	License_Citation *string `json:"license_citation"`
	Language         *string `json:"language"`
	/* Here we have a problem in spec 2.1. which says "nach ISO\IEC 10646-1", which means utf-8, utf-16 and utf-32.
	 * We would certainly support more encodings, as eg.
	 * ISO 19115 / B.5.10 MD_CharacterSetCode<> or
	 * http://www.iana.org/assignments/character-sets/character-sets.xml
	 */
	Encoding *string `json:"characterset"`
}

type MetaData struct {
	// Core
	Title       string `json:"title"`
	Description string `json:"notes"`
	Schlagworte []Tags `json:"tags"`
	Maintainer  string `json:"maintainer"`
	License     string `json:"license"` // Sollte URI des Lizenzdokuments sein

	// nested structs
	Extras   `json:"extras"`
	Resource []Resource `json:"resources"`
}

func init() {
	for idx, val := range categories {
		categorymap[val.ID] = categories[idx]
	}
	ogdat.Register(Version21, specfile)
}
<|endoftext|>


<commit_before>package ipState

import (
	"bytes"
	"encoding/json"
	"net/http"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/nitro/memberlist"
)

type GlobalMap struct {
	IpLive    map[string]bool // true if an IP is still alive and false otherwise
	Mutex     *sync.RWMutex
	Gossip    *memberlist.Memberlist
	Peers     []string
	LocalAddr string
	Clustered bool
	Members   *memberlist.Memberlist
}

type HttpUpdate struct {
	Peer      string
	IpAddress string
	Live      bool
}

var Gm *GlobalMap

var Mutex *sync.RWMutex

func InitGM(ipAddresses []string) {
	m := make(map[string]bool)
	for _, ip := range ipAddresses {
		m[ip] = true
	}

	Gm = &GlobalMap{IpLive: m, Mutex: &sync.RWMutex{}}
}

//NotifyPeers will update all peers on an IP address state change
func notifyPeers(ipAddress string, live bool) error {
	for _, peer := range Gm.Peers {
		log.Debugln("Send update to peer:", peer)
		upd := &HttpUpdate{Peer: Gm.LocalAddr, IpAddress: ipAddress, Live: live}
		data, err := json.Marshal(upd)
		if err != nil {
			log.Errorln("Error Marshaling", err)
			return err
		}
		buff := bytes.NewBuffer(data)
		req, err := http.NewRequest("POST", "http://"+peer, buff)
		req.Close = true
		req.Header.Set("Content-Type", "application/json")
		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil {
			log.Errorln("HTTP Post Error", err)
			return err
		}
		defer resp.Body.Close()
	}
	log.Debugln("Sent update to Peers", Gm.Peers)
	return nil
}

func NotifyIpState(ipAddress string, live bool, peerUpdate bool) error {
	Gm.Mutex.RLock()
	//check := Gm.IpLive[ipAddress]
	//Gm.Mutex.RUnlock()
	if Gm.IpLive[ipAddress] == live && peerUpdate == true { //if we have seen the failure and received a notification from a peer that they've seen the failure
		Gm.Mutex.RUnlock()
		log.Debugln("Received agreement from Peer.  Removing IP")
		if live == false {
			err := master.dns.RemoveIP(ipAddress)
			if err != nil {
				log.Errorln("Error Removing IP: ", ipAddress, err)
			}
		} else {
			err := master.dns.AddIP(ipAddress)
			if err != nil {
				log.Errorln("Error Adding IP: ", ipAddress, err)
			}
		}
	} else if peerUpdate == false { //we haven't received a message from a peer yet so just update the state and notify peers
		Gm.Mutex.RUnlock()
		Gm.Mutex.Lock()
		Gm.IpLive[ipAddress] = live
		Gm.Mutex.Unlock()
		log.Debugln("Notifying peers: ", ipAddress, live)
		err := notifyPeers(ipAddress, live)
		if err != nil {
			log.Errorln("Error notifying Peers", err)
		}
	}
	return nil
}
<commit_msg>fixes<commit_after>package ipState

import (
	"bytes"
	"encoding/json"
	"net/http"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/nitro/memberlist"
)

type GlobalMap struct {
	IpLive    map[string]bool // true if an IP is still alive and false otherwise
	Mutex     *sync.RWMutex
	Gossip    *memberlist.Memberlist
	Peers     []string
	LocalAddr string
	Clustered bool
	Members   *memberlist.Memberlist
}

type HttpUpdate struct {
	Peer      string
	IpAddress string
	Live      bool
}

var Gm *GlobalMap

var Mutex *sync.RWMutex

func InitGM(ipAddresses []string) {
	m := make(map[string]bool)
	for _, ip := range ipAddresses {
		m[ip] = true
	}

	Gm = &GlobalMap{IpLive: m, Mutex: &sync.RWMutex{}}
}

//NotifyPeers will update all peers on an IP address state change
func notifyPeers(ipAddress string, live bool) error {
	for _, peer := range Gm.Peers {
		log.Debugln("Send update to peer:", peer)
		upd := &HttpUpdate{Peer: Gm.LocalAddr, IpAddress: ipAddress, Live: live}
		data, err := json.Marshal(upd)
		if err != nil {
			log.Errorln("Error Marshaling", err)
			return err
		}
		buff := bytes.NewBuffer(data)
		req, err := http.NewRequest("POST", "http://"+peer, buff)
		req.Close = true
		req.Header.Set("Content-Type", "application/json")
		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil {
			log.Errorln("HTTP Post Error", err)
			return err
		}
		defer resp.Body.Close()
	}
	log.Debugln("Sent update to Peers", Gm.Peers)
	return nil
}

func NotifyIpState(ipAddress string, live bool, peerUpdate bool) error {
	Gm.Mutex.RLock()
	//check := Gm.IpLive[ipAddress]
	//Gm.Mutex.RUnlock()
	if Gm.IpLive[ipAddress] == live && peerUpdate == true { //if we have seen the failure and received a notification from a peer that they've seen the failure
		Gm.Mutex.RUnlock()
		log.Debugln("Received agreement from Peer.  Removing IP")
		if live == false {
			err := master.dns.RemoveIP(ipAddress)
			if err != nil {
				log.Errorln("Error Removing IP: ", ipAddress, err)
			}
		} else {
			err := master.dns.AddIP(ipAddress)
			if err != nil {
				log.Errorln("Error Adding IP: ", ipAddress, err)
			}
		}
	} else if peerUpdate == false { //we haven't received a message from a peer yet so just update the state and notify peers
		Gm.Mutex.RUnlock()
		Gm.Mutex.Lock()
		Gm.IpLive[ipAddress] = live
		Gm.Mutex.Unlock()
		log.Debugln("Notifying peers: ", ipAddress, live)
		err := notifyPeers(ipAddress, live)
		if err != nil {
			log.Errorln("Error notifying Peers", err)
		}
	}
	Gm.Mutex.RUnlock()
	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"os"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/redshift"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSRedshiftSecurityGroup_basic(t *testing.T) {
	// This is necessary to prevent "VPC-by-Default customers cannot use cluster security groups" errors
	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)

	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidr(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSRedshiftSecurityGroup_ingressCidr(t *testing.T) {
	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidr(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("redshift-sg-terraform-%d", rInt)),
					resource.TestCheckResourceAttr(
						resourceName, "description", "Managed by Terraform"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.2735652665.cidr", "10.0.0.1/24"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSRedshiftSecurityGroup_updateIngressCidr(t *testing.T) {
	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidr(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidrAdd(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "3"),
				),
			},
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidrReduce(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "2"),
				),
			},
		},
	})
}

func TestAccAWSRedshiftSecurityGroup_ingressSecurityGroup(t *testing.T) {
	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressSgId(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("redshift-sg-terraform-%d", rInt)),
					resource.TestCheckResourceAttr(
						resourceName, "description", "this is a description"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSRedshiftSecurityGroup_updateIngressSecurityGroup(t *testing.T) {
	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressSgId(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressSgIdAdd(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "3"),
				),
			},
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressSgIdReduce(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "2"),
				),
			},
		},
	})
}

func testAccCheckAWSRedshiftSecurityGroupExists(n string, v *redshift.ClusterSecurityGroup) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Redshift Security Group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).redshiftconn

		opts := redshift.DescribeClusterSecurityGroupsInput{
			ClusterSecurityGroupName: aws.String(rs.Primary.ID),
		}

		resp, err := conn.DescribeClusterSecurityGroups(&opts)

		if err != nil {
			return err
		}

		if len(resp.ClusterSecurityGroups) != 1 ||
			*resp.ClusterSecurityGroups[0].ClusterSecurityGroupName != rs.Primary.ID {
			return fmt.Errorf("Redshift Security Group not found")
		}

		*v = *resp.ClusterSecurityGroups[0]

		return nil
	}
}

func testAccCheckAWSRedshiftSecurityGroupDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).redshiftconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_redshift_security_group" {
			continue
		}

		// Try to find the Group
		resp, err := conn.DescribeClusterSecurityGroups(
			&redshift.DescribeClusterSecurityGroupsInput{
				ClusterSecurityGroupName: aws.String(rs.Primary.ID),
			})

		if err == nil {
			if len(resp.ClusterSecurityGroups) != 0 &&
				*resp.ClusterSecurityGroups[0].ClusterSecurityGroupName == rs.Primary.ID {
				return fmt.Errorf("Redshift Security Group still exists")
			}
		}

		// Verify the error
		newerr, ok := err.(awserr.Error)
		if !ok {
			return err
		}
		if newerr.Code() != "ClusterSecurityGroupNotFound" {
			return err
		}
	}

	return nil
}

func TestResourceAWSRedshiftSecurityGroupNameValidation(t *testing.T) {
	cases := []struct {
		Value    string
		ErrCount int
	}{
		{
			Value:    "default",
			ErrCount: 1,
		},
		{
			Value:    "testing123%%",
			ErrCount: 1,
		},
		{
			Value:    "TestingSG",
			ErrCount: 1,
		},
		{
			Value:    randomString(256),
			ErrCount: 1,
		},
	}

	for _, tc := range cases {
		_, errors := validateRedshiftSecurityGroupName(tc.Value, "aws_redshift_security_group_name")

		if len(errors) != tc.ErrCount {
			t.Fatalf("Expected the Redshift Security Group Name to trigger a validation error")
		}
	}
}

func testAccAWSRedshiftSecurityGroupConfig_ingressCidr(rInt int) string {
	return fmt.Sprintf(`
provider "aws" {
  region = "us-east-1"
}

resource "aws_redshift_security_group" "test" {
  name = "redshift-sg-terraform-%d"

  ingress {
    cidr = "10.0.0.1/24"
  }
}
`, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressCidrAdd(rInt int) string {
	return fmt.Sprintf(`
provider "aws" {
  region = "us-east-1"
}

resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    cidr = "10.0.0.1/24"
  }

  ingress {
    cidr = "10.0.10.1/24"
  }

  ingress {
    cidr = "10.0.20.1/24"
  }
}
`, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressCidrReduce(rInt int) string {
	return fmt.Sprintf(`
provider "aws" {
  region = "us-east-1"
}

resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    cidr = "10.0.0.1/24"
  }

  ingress {
    cidr = "10.0.10.1/24"
  }
}
`, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressSgId(rInt int) string {
	return fmt.Sprintf(`
provider "aws" {
  region = "us-east-1"
}

resource "aws_security_group" "redshift" {
  name        = "terraform_redshift_test_%d"
  description = "Used in the redshift acceptance tests"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.0.0.0/8"]
  }
}

resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    security_group_name     = "${aws_security_group.redshift.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }
}
`, rInt, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressSgIdAdd(rInt int) string {
	return fmt.Sprintf(`
provider "aws" {
  region = "us-east-1"
}

resource "aws_security_group" "redshift" {
  name        = "terraform_redshift_test_%d"
  description = "Used in the redshift acceptance tests"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.0.0.0/16"]
  }
}

resource "aws_security_group" "redshift2" {
  name        = "terraform_redshift_test_2_%d"
  description = "Used in the redshift acceptance tests #2"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.1.0.0/16"]
  }
}

resource "aws_security_group" "redshift3" {
  name        = "terraform_redshift_test_3_%d"
  description = "Used in the redshift acceptance tests #3"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.2.0.0/16"]
  }
}

resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    security_group_name     = "${aws_security_group.redshift.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }

  ingress {
    security_group_name     = "${aws_security_group.redshift2.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }

  ingress {
    security_group_name     = "${aws_security_group.redshift3.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }
}
`, rInt, rInt, rInt, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressSgIdReduce(rInt int) string {
	return fmt.Sprintf(`
provider "aws" {
  region = "us-east-1"
}

resource "aws_security_group" "redshift" {
  name        = "terraform_redshift_test_%d"
  description = "Used in the redshift acceptance tests"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.0.0.0/16"]
  }
}

resource "aws_security_group" "redshift2" {
  name        = "terraform_redshift_test_2_%d"
  description = "Used in the redshift acceptance tests #2"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.1.0.0/16"]
  }
}

resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    security_group_name     = "${aws_security_group.redshift.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }

  ingress {
    security_group_name     = "${aws_security_group.redshift2.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }
}
`, rInt, rInt, rInt)
}
<commit_msg>Fix aws region for teamcity<commit_after>package aws

import (
	"fmt"
	"os"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/redshift"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSRedshiftSecurityGroup_basic(t *testing.T) {
	// This is necessary to prevent "VPC-by-Default customers cannot use cluster security groups" errors
	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)

	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidr(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSRedshiftSecurityGroup_ingressCidr(t *testing.T) {
	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)

	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidr(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("redshift-sg-terraform-%d", rInt)),
					resource.TestCheckResourceAttr(
						resourceName, "description", "Managed by Terraform"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.2735652665.cidr", "10.0.0.1/24"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSRedshiftSecurityGroup_updateIngressCidr(t *testing.T) {
	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)

	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidr(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidrAdd(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "3"),
				),
			},
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressCidrReduce(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "2"),
				),
			},
		},
	})
}

func TestAccAWSRedshiftSecurityGroup_ingressSecurityGroup(t *testing.T) {
	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)

	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressSgId(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("redshift-sg-terraform-%d", rInt)),
					resource.TestCheckResourceAttr(
						resourceName, "description", "this is a description"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSRedshiftSecurityGroup_updateIngressSecurityGroup(t *testing.T) {
	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)

	var v redshift.ClusterSecurityGroup
	rInt := acctest.RandInt()
	resourceName := "aws_redshift_security_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSRedshiftSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressSgId(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressSgIdAdd(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "3"),
				),
			},
			{
				Config: testAccAWSRedshiftSecurityGroupConfig_ingressSgIdReduce(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSRedshiftSecurityGroupExists(resourceName, &v),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "2"),
				),
			},
		},
	})
}

func testAccCheckAWSRedshiftSecurityGroupExists(n string, v *redshift.ClusterSecurityGroup) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Redshift Security Group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).redshiftconn

		opts := redshift.DescribeClusterSecurityGroupsInput{
			ClusterSecurityGroupName: aws.String(rs.Primary.ID),
		}

		resp, err := conn.DescribeClusterSecurityGroups(&opts)

		if err != nil {
			return err
		}

		if len(resp.ClusterSecurityGroups) != 1 ||
			*resp.ClusterSecurityGroups[0].ClusterSecurityGroupName != rs.Primary.ID {
			return fmt.Errorf("Redshift Security Group not found")
		}

		*v = *resp.ClusterSecurityGroups[0]

		return nil
	}
}

func testAccCheckAWSRedshiftSecurityGroupDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).redshiftconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_redshift_security_group" {
			continue
		}

		// Try to find the Group
		resp, err := conn.DescribeClusterSecurityGroups(
			&redshift.DescribeClusterSecurityGroupsInput{
				ClusterSecurityGroupName: aws.String(rs.Primary.ID),
			})

		if err == nil {
			if len(resp.ClusterSecurityGroups) != 0 &&
				*resp.ClusterSecurityGroups[0].ClusterSecurityGroupName == rs.Primary.ID {
				return fmt.Errorf("Redshift Security Group still exists")
			}
		}

		// Verify the error
		newerr, ok := err.(awserr.Error)
		if !ok {
			return err
		}
		if newerr.Code() != "ClusterSecurityGroupNotFound" {
			return err
		}
	}

	return nil
}

func TestResourceAWSRedshiftSecurityGroupNameValidation(t *testing.T) {
	cases := []struct {
		Value    string
		ErrCount int
	}{
		{
			Value:    "default",
			ErrCount: 1,
		},
		{
			Value:    "testing123%%",
			ErrCount: 1,
		},
		{
			Value:    "TestingSG",
			ErrCount: 1,
		},
		{
			Value:    randomString(256),
			ErrCount: 1,
		},
	}

	for _, tc := range cases {
		_, errors := validateRedshiftSecurityGroupName(tc.Value, "aws_redshift_security_group_name")

		if len(errors) != tc.ErrCount {
			t.Fatalf("Expected the Redshift Security Group Name to trigger a validation error")
		}
	}
}

func testAccAWSRedshiftSecurityGroupConfig_ingressCidr(rInt int) string {
	return fmt.Sprintf(`
resource "aws_redshift_security_group" "test" {
  name = "redshift-sg-terraform-%d"

  ingress {
    cidr = "10.0.0.1/24"
  }
}
`, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressCidrAdd(rInt int) string {
	return fmt.Sprintf(`
resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    cidr = "10.0.0.1/24"
  }

  ingress {
    cidr = "10.0.10.1/24"
  }

  ingress {
    cidr = "10.0.20.1/24"
  }
}
`, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressCidrReduce(rInt int) string {
	return fmt.Sprintf(`
resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    cidr = "10.0.0.1/24"
  }

  ingress {
    cidr = "10.0.10.1/24"
  }
}
`, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressSgId(rInt int) string {
	return fmt.Sprintf(`
resource "aws_security_group" "redshift" {
  name        = "terraform_redshift_test_%d"
  description = "Used in the redshift acceptance tests"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.0.0.0/8"]
  }
}

resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    security_group_name     = "${aws_security_group.redshift.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }
}
`, rInt, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressSgIdAdd(rInt int) string {
	return fmt.Sprintf(`
resource "aws_security_group" "redshift" {
  name        = "terraform_redshift_test_%d"
  description = "Used in the redshift acceptance tests"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.0.0.0/16"]
  }
}

resource "aws_security_group" "redshift2" {
  name        = "terraform_redshift_test_2_%d"
  description = "Used in the redshift acceptance tests #2"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.1.0.0/16"]
  }
}

resource "aws_security_group" "redshift3" {
  name        = "terraform_redshift_test_3_%d"
  description = "Used in the redshift acceptance tests #3"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.2.0.0/16"]
  }
}

resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    security_group_name     = "${aws_security_group.redshift.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }

  ingress {
    security_group_name     = "${aws_security_group.redshift2.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }

  ingress {
    security_group_name     = "${aws_security_group.redshift3.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }
}
`, rInt, rInt, rInt, rInt)
}

func testAccAWSRedshiftSecurityGroupConfig_ingressSgIdReduce(rInt int) string {
	return fmt.Sprintf(`
resource "aws_security_group" "redshift" {
  name        = "terraform_redshift_test_%d"
  description = "Used in the redshift acceptance tests"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.0.0.0/16"]
  }
}

resource "aws_security_group" "redshift2" {
  name        = "terraform_redshift_test_2_%d"
  description = "Used in the redshift acceptance tests #2"

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.1.0.0/16"]
  }
}

resource "aws_redshift_security_group" "test" {
  name        = "redshift-sg-terraform-%d"
  description = "this is a description"

  ingress {
    security_group_name     = "${aws_security_group.redshift.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }

  ingress {
    security_group_name     = "${aws_security_group.redshift2.name}"
    security_group_owner_id = "${aws_security_group.redshift.owner_id}"
  }
}
`, rInt, rInt, rInt)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package admission

import (
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)

// alwaysAdmitController says yes to all admission control requests, its useful for testing.
type alwaysAdmitController struct{}

func (alwaysAdmitController) AdmissionControl(operation, kind, namespace string, object runtime.Object) (err error) {
	return nil
}

func NewAlwaysAdmitController() AdmissionControl {
	return new(alwaysAdmitController)
}

type admissionController struct {
	client           client.Interface
	admissionHandler Interface
}

func NewAdmissionControl(client client.Interface, pluginNames []string, configFilePath string) AdmissionControl {
	return NewAdmissionControlForHandler(client, newInterface(pluginNames, configFilePath))
}

func NewAdmissionControlForHandler(client client.Interface, handler Interface) AdmissionControl {
	return &admissionController{
		client:           client,
		admissionHandler: handler,
	}
}

func (ac *admissionController) AdmissionControl(operation, kind, namespace string, object runtime.Object) (err error) {
	return ac.admissionHandler.Admit(NewAttributesRecord(ac.client, object, namespace, kind, operation))
}
<commit_msg>Fix boilerplate and make stub controller<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package admission

import (
	"errors"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)

// stubAdmissionController is capable of either always admitting or always denying incoming requests
type stubAdmissionController struct {
	admit bool
}

func (ac *stubAdmissionController) AdmissionControl(operation, kind, namespace string, object runtime.Object) (err error) {
	if !ac.admit {
		err = errors.New("No changes allowed")
	}
	return err
}

func NewAlwaysAdmitController() AdmissionControl {
	return &stubAdmissionController{
		admit: true,
	}
}

func NewAlwaysDenyController() AdmissionControl {
	return &stubAdmissionController{
		admit: false,
	}
}

type admissionController struct {
	client           client.Interface
	admissionHandler Interface
}

func NewAdmissionControl(client client.Interface, pluginNames []string, configFilePath string) AdmissionControl {
	return NewAdmissionControlForHandler(client, newInterface(pluginNames, configFilePath))
}

func NewAdmissionControlForHandler(client client.Interface, handler Interface) AdmissionControl {
	return &admissionController{
		client:           client,
		admissionHandler: handler,
	}
}

func (ac *admissionController) AdmissionControl(operation, kind, namespace string, object runtime.Object) (err error) {
	return ac.admissionHandler.Admit(NewAttributesRecord(ac.client, object, namespace, kind, operation))
}
<|endoftext|>


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

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

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

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

package bootstrapper

import (
	"net"
	"path"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"github.com/pkg/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/client-go/tools/clientcmd/api/latest"
	"k8s.io/minikube/pkg/minikube/assets"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/util"
	"k8s.io/minikube/pkg/util/kubeconfig"
)

var (
	certs = []string{
		"ca.crt", "ca.key", "apiserver.crt", "apiserver.key", "proxy-client-ca.crt",
		"proxy-client-ca.key", "proxy-client.crt", "proxy-client.key",
	}
)

// SetupCerts gets the generated credentials required to talk to the APIServer.
func SetupCerts(cmd CommandRunner, k8s KubernetesConfig) error {
	localPath := constants.GetMinipath()
	glog.Infof("Setting up certificates for IP: %s\n", k8s.NodeIP)

	if err := generateCerts(k8s); err != nil {
		return errors.Wrap(err, "Error generating certs")
	}

	copyableFiles := []assets.CopyableFile{}

	for _, cert := range certs {
		p := filepath.Join(localPath, cert)
		perms := "0644"
		if strings.HasSuffix(cert, ".key") {
			perms = "0600"
		}
		certFile, err := assets.NewFileAsset(p, util.DefaultCertPath, cert, perms)
		if err != nil {
			return err
		}
		copyableFiles = append(copyableFiles, certFile)
	}

	kubeCfgSetup := &kubeconfig.KubeConfigSetup{
		ClusterName:          k8s.NodeName,
		ClusterServerAddress: "https://localhost:8443",
		ClientCertificate:    path.Join(util.DefaultCertPath, "apiserver.crt"),
		ClientKey:            path.Join(util.DefaultCertPath, "apiserver.key"),
		CertificateAuthority: path.Join(util.DefaultCertPath, "ca.crt"),
		KeepContext:          false,
	}

	kubeCfg := api.NewConfig()
	kubeconfig.PopulateKubeConfig(kubeCfgSetup, kubeCfg)
	data, err := runtime.Encode(latest.Codec, kubeCfg)
	if err != nil {
		return errors.Wrap(err, "encoding kubeconfig")
	}

	kubeCfgFile := assets.NewMemoryAsset(data,
		util.DefaultLocalkubeDirectory, "kubeconfig", "0644")
	copyableFiles = append(copyableFiles, kubeCfgFile)

	for _, f := range copyableFiles {
		if err := cmd.Copy(f); err != nil {
			return err
		}
	}
	return nil
}

func generateCerts(k8s KubernetesConfig) error {
	serviceIP, err := util.GetServiceClusterIP(k8s.ServiceCIDR)
	if err != nil {
		return errors.Wrap(err, "getting service cluster ip")
	}

	localPath := constants.GetMinipath()

	caCertPath := filepath.Join(localPath, "ca.crt")
	caKeyPath := filepath.Join(localPath, "ca.key")

	proxyClientCACertPath := filepath.Join(localPath, "proxy-client-ca.crt")
	proxyClientCAKeyPath := filepath.Join(localPath, "proxy-client-ca.key")

	caCertSpecs := []struct {
		certPath string
		keyPath  string
		subject  string
	}{
		{ // client / apiserver CA
			certPath: caCertPath,
			keyPath:  caKeyPath,
			subject:  k8s.APIServerName,
		},
		{ // proxy-client CA
			certPath: proxyClientCACertPath,
			keyPath:  proxyClientCAKeyPath,
			subject:  "proxyClientCA",
		},
	}

	signedCertSpecs := []struct {
		certPath       string
		keyPath        string
		subject        string
		ips            []net.IP
		alternateNames []string
		caCertPath     string
		caKeyPath      string
	}{
		{ // Client cert
			certPath:       filepath.Join(localPath, "client.crt"),
			keyPath:        filepath.Join(localPath, "client.key"),
			subject:        "minikube-user",
			ips:            []net.IP{},
			alternateNames: []string{},
			caCertPath:     caCertPath,
			caKeyPath:      caKeyPath,
		},
		{ // apiserver serving cert
			certPath:       filepath.Join(localPath, "apiserver.crt"),
			keyPath:        filepath.Join(localPath, "apiserver.key"),
			subject:        "minikube",
			ips:            []net.IP{net.ParseIP(k8s.NodeIP), serviceIP, net.ParseIP("10.0.0.1")},
			alternateNames: util.GetAlternateDNS(k8s.DNSDomain),
			caCertPath:     caCertPath,
			caKeyPath:      caKeyPath,
		},
		{ // aggregator proxy-client cert
			certPath:       filepath.Join(localPath, "proxy-client.crt"),
			keyPath:        filepath.Join(localPath, "proxy-client.key"),
			subject:        "aggregator",
			ips:            []net.IP{},
			alternateNames: []string{},
			caCertPath:     proxyClientCACertPath,
			caKeyPath:      proxyClientCAKeyPath,
		},
	}

	for _, caCertSpec := range caCertSpecs {
		if !(util.CanReadFile(caCertSpec.certPath) &&
			util.CanReadFile(caCertSpec.keyPath)) {
			if err := util.GenerateCACert(
				caCertSpec.certPath, caCertSpec.keyPath, caCertSpec.subject,
			); err != nil {
				return errors.Wrap(err, "Error generating CA certificate")
			}
		}
	}

	for _, signedCertSpec := range signedCertSpecs {
		if err := util.GenerateSignedCert(
			signedCertSpec.certPath, signedCertSpec.keyPath, signedCertSpec.subject,
			signedCertSpec.ips, signedCertSpec.alternateNames,
			signedCertSpec.caCertPath, signedCertSpec.caKeyPath,
		); err != nil {
			return errors.Wrap(err, "Error generating signed apiserver serving cert")
		}
	}

	return nil
}
<commit_msg>Fix the usage of --apiserver-name<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package bootstrapper

import (
	"net"
	"path"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"github.com/pkg/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/client-go/tools/clientcmd/api/latest"
	"k8s.io/minikube/pkg/minikube/assets"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/util"
	"k8s.io/minikube/pkg/util/kubeconfig"
)

var (
	certs = []string{
		"ca.crt", "ca.key", "apiserver.crt", "apiserver.key", "proxy-client-ca.crt",
		"proxy-client-ca.key", "proxy-client.crt", "proxy-client.key",
	}
)

// SetupCerts gets the generated credentials required to talk to the APIServer.
func SetupCerts(cmd CommandRunner, k8s KubernetesConfig) error {
	localPath := constants.GetMinipath()
	glog.Infof("Setting up certificates for IP: %s\n", k8s.NodeIP)

	if err := generateCerts(k8s); err != nil {
		return errors.Wrap(err, "Error generating certs")
	}

	copyableFiles := []assets.CopyableFile{}

	for _, cert := range certs {
		p := filepath.Join(localPath, cert)
		perms := "0644"
		if strings.HasSuffix(cert, ".key") {
			perms = "0600"
		}
		certFile, err := assets.NewFileAsset(p, util.DefaultCertPath, cert, perms)
		if err != nil {
			return err
		}
		copyableFiles = append(copyableFiles, certFile)
	}

	kubeCfgSetup := &kubeconfig.KubeConfigSetup{
		ClusterName:          k8s.NodeName,
		ClusterServerAddress: "https://localhost:8443",
		ClientCertificate:    path.Join(util.DefaultCertPath, "apiserver.crt"),
		ClientKey:            path.Join(util.DefaultCertPath, "apiserver.key"),
		CertificateAuthority: path.Join(util.DefaultCertPath, "ca.crt"),
		KeepContext:          false,
	}

	kubeCfg := api.NewConfig()
	kubeconfig.PopulateKubeConfig(kubeCfgSetup, kubeCfg)
	data, err := runtime.Encode(latest.Codec, kubeCfg)
	if err != nil {
		return errors.Wrap(err, "encoding kubeconfig")
	}

	kubeCfgFile := assets.NewMemoryAsset(data,
		util.DefaultLocalkubeDirectory, "kubeconfig", "0644")
	copyableFiles = append(copyableFiles, kubeCfgFile)

	for _, f := range copyableFiles {
		if err := cmd.Copy(f); err != nil {
			return err
		}
	}
	return nil
}

func generateCerts(k8s KubernetesConfig) error {
	serviceIP, err := util.GetServiceClusterIP(k8s.ServiceCIDR)
	if err != nil {
		return errors.Wrap(err, "getting service cluster ip")
	}

	localPath := constants.GetMinipath()

	caCertPath := filepath.Join(localPath, "ca.crt")
	caKeyPath := filepath.Join(localPath, "ca.key")

	proxyClientCACertPath := filepath.Join(localPath, "proxy-client-ca.crt")
	proxyClientCAKeyPath := filepath.Join(localPath, "proxy-client-ca.key")

	caCertSpecs := []struct {
		certPath string
		keyPath  string
		subject  string
	}{
		{ // client / apiserver CA
			certPath: caCertPath,
			keyPath:  caKeyPath,
			subject:  "minikubeCA",
		},
		{ // proxy-client CA
			certPath: proxyClientCACertPath,
			keyPath:  proxyClientCAKeyPath,
			subject:  "proxyClientCA",
		},
	}

	signedCertSpecs := []struct {
		certPath       string
		keyPath        string
		subject        string
		ips            []net.IP
		alternateNames []string
		caCertPath     string
		caKeyPath      string
	}{
		{ // Client cert
			certPath:       filepath.Join(localPath, "client.crt"),
			keyPath:        filepath.Join(localPath, "client.key"),
			subject:        "minikube-user",
			ips:            []net.IP{},
			alternateNames: []string{},
			caCertPath:     caCertPath,
			caKeyPath:      caKeyPath,
		},
		{ // apiserver serving cert
			certPath:       filepath.Join(localPath, "apiserver.crt"),
			keyPath:        filepath.Join(localPath, "apiserver.key"),
			subject:        k8s.APIServerName,
			ips:            []net.IP{net.ParseIP(k8s.NodeIP), serviceIP, net.ParseIP("10.0.0.1")},
			alternateNames: util.GetAlternateDNS(k8s.DNSDomain),
			caCertPath:     caCertPath,
			caKeyPath:      caKeyPath,
		},
		{ // aggregator proxy-client cert
			certPath:       filepath.Join(localPath, "proxy-client.crt"),
			keyPath:        filepath.Join(localPath, "proxy-client.key"),
			subject:        "aggregator",
			ips:            []net.IP{},
			alternateNames: []string{},
			caCertPath:     proxyClientCACertPath,
			caKeyPath:      proxyClientCAKeyPath,
		},
	}

	for _, caCertSpec := range caCertSpecs {
		if !(util.CanReadFile(caCertSpec.certPath) &&
			util.CanReadFile(caCertSpec.keyPath)) {
			if err := util.GenerateCACert(
				caCertSpec.certPath, caCertSpec.keyPath, caCertSpec.subject,
			); err != nil {
				return errors.Wrap(err, "Error generating CA certificate")
			}
		}
	}

	for _, signedCertSpec := range signedCertSpecs {
		if err := util.GenerateSignedCert(
			signedCertSpec.certPath, signedCertSpec.keyPath, signedCertSpec.subject,
			signedCertSpec.ips, signedCertSpec.alternateNames,
			signedCertSpec.caCertPath, signedCertSpec.caKeyPath,
		); err != nil {
			return errors.Wrap(err, "Error generating signed apiserver serving cert")
		}
	}

	return nil
}
<|endoftext|>