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

package java

import (
	"bytes"
	"log"
	"path"
	"strings"

	"v.io/x/ref/lib/vdl/compile"
	"v.io/x/ref/lib/vdl/vdlutil"
)

const serverWrapperTmpl = header + `
// Source(s):  {{ .Source }}
package {{ .PackagePath }};

/**
 * Wrapper for {@link {{ .ServiceName }}Server}.  This wrapper is used by
 * {@link io.v.v23.rpc.ReflectInvoker} to indirectly invoke server methods.
 */
public final class {{ .ServiceName }}ServerWrapper {
    private final {{ .FullServiceName }}Server server;

{{/* Define fields to hold each of the embedded server wrappers*/}}
{{ range $embed := .Embeds }}
    {{/* e.g. private final com.somepackage.gen_impl.ArithStub stubArith; */}}
    private final {{ $embed.FullName }}ServerWrapper wrapper{{ $embed.Name }};
    {{ end }}

    /**
     * Creates a new {@link {{ .ServiceName }}ServerWrapper} to invoke the methods of the
     * provided server.
     *
     * @param server server whose methods are to be invoked
     */
    public {{ .ServiceName }}ServerWrapper({{ .FullServiceName }}Server server) {
        this.server = server;
        {{/* Initialize the embeded server wrappers */}}
        {{ range $embed := .Embeds }}
        this.wrapper{{ $embed.Name }} = new {{ $embed.FullName }}ServerWrapper(server);
        {{ end }}
    }

    /**
     * Returns a description of this server.
     */
    public io.v.v23.vdlroot.signature.Interface signature() {
        java.util.List<io.v.v23.vdlroot.signature.Embed> embeds = new java.util.ArrayList<io.v.v23.vdlroot.signature.Embed>();
        java.util.List<io.v.v23.vdlroot.signature.Method> methods = new java.util.ArrayList<io.v.v23.vdlroot.signature.Method>();
        {{ range $method := .Methods }}
        {
            java.util.List<io.v.v23.vdlroot.signature.Arg> inArgs = new java.util.ArrayList<io.v.v23.vdlroot.signature.Arg>();
            {{ range $arg := $method.CallingArgTypes }}
            inArgs.add(new io.v.v23.vdlroot.signature.Arg("", "", new io.v.v23.vdl.VdlTypeObject({{ $arg }})));
            {{ end }}
            java.util.List<io.v.v23.vdlroot.signature.Arg> outArgs = new java.util.ArrayList<io.v.v23.vdlroot.signature.Arg>();
            {{ range $arg := $method.RetJavaTypes }}
            outArgs.add(new io.v.v23.vdlroot.signature.Arg("", "", new io.v.v23.vdl.VdlTypeObject({{ $arg }})));
            {{ end }}
            java.util.List<io.v.v23.vdl.VdlAny> tags = new java.util.ArrayList<io.v.v23.vdl.VdlAny>();
            {{ range $tag := .Tags }}
            tags.add(new io.v.v23.vdl.VdlAny(io.v.v23.vdl.VdlValue.valueOf({{ $tag.Value }}, {{ $tag.Type }})));
            {{ end }}
            methods.add(new io.v.v23.vdlroot.signature.Method(
                "{{ $method.Name }}",
                "{{ $method.Doc }}",
                inArgs,
                outArgs,
                null,
                null,
                tags));
        }
        {{ end }}

        return new io.v.v23.vdlroot.signature.Interface("{{ .ServiceName }}", "{{ .PackagePath }}", "{{ .Doc }}", embeds, methods);
    }

    /**
     * Returns all tags associated with the provided method or {@code null} if the method isn't
     * implemented by this server.
     *
     * @param method method whose tags are to be returned
     */
    @SuppressWarnings("unused")
    public io.v.v23.vdl.VdlValue[] getMethodTags(java.lang.String method) throws io.v.v23.verror.VException {
        {{ range $methodName, $tags := .MethodTags }}
        if ("{{ $methodName }}".equals(method)) {
            try {
                return new io.v.v23.vdl.VdlValue[] {
                    {{ range $tag := $tags }} io.v.v23.vdl.VdlValue.valueOf({{ $tag.Value }}, {{ $tag.Type }}), {{ end }}
                };
            } catch (IllegalArgumentException e) {
                throw new io.v.v23.verror.VException(String.format("Couldn't get tags for method \"{{ $methodName }}\": %s", e.getMessage()));
            }
        }
        {{ end }}
        {{ range $embed := .Embeds }}
        {
            io.v.v23.vdl.VdlValue[] tags = this.wrapper{{ $embed.Name }}.getMethodTags(method);
            if (tags != null) {
                return tags;
            }
        }
        {{ end }}
        return null;  // method not found
    }

     {{/* Iterate over methods defined directly in the body of this server */}}
    {{ range $method := .Methods }}
    {{ $method.JavaDoc }}
    public {{ $method.RetType }} {{ $method.Name }}(io.v.v23.context.VContext _ctx, final io.v.v23.rpc.StreamServerCall _call{{ $method.DeclarationArgs }}) throws io.v.v23.verror.VException {
        {{ if $method.IsStreaming }}
        final io.v.v23.rpc.StreamIterable<{{ $method.RecvType }}> _it = new io.v.v23.rpc.StreamIterable(_call,new com.google.common.reflect.TypeToken<{{ $method.RecvType }}>() {}.getType());
        io.v.v23.vdl.ServerStream<{{ $method.SendType }}, {{ $method.RecvType }}> _stream = new io.v.v23.vdl.ServerStream<{{ $method.SendType }}, {{ $method.RecvType }}>() {
            @Override
            public void send({{ $method.SendType }} item) throws io.v.v23.verror.VException {
                java.lang.reflect.Type type = new com.google.common.reflect.TypeToken< {{ $method.SendType }} >() {}.getType();
                _call.send(item, type);
            }
            @Override
            public java.util.Iterator<{{ $method.RecvType }}> iterator() {
                return _it.iterator();
            }
            @Override
            public io.v.v23.verror.VException error() {
                return _it.error();
            }
        };
        {{ end }} {{/* end if $method.IsStreaming */}}
        {{ if $method.Returns }} return {{ end }} this.server.{{ $method.Name }}(_ctx, _call {{ $method.CallingArgs }} {{ if $method.IsStreaming }} ,_stream {{ end }} );
    }
{{end}}

{{/* Iterate over methods from embeded servers and generate code to delegate the work */}}
{{ range $eMethod := .EmbedMethods }}
    {{ $eMethod.JavaDoc }}
    public {{ $eMethod.RetType }} {{ $eMethod.Name }}(io.v.v23.context.VContext ctx, io.v.v23.rpc.StreamServerCall call{{ $eMethod.DeclarationArgs }}) throws io.v.v23.verror.VException {
        {{/* e.g. return this.stubArith.cosine(ctx, call, [args], options) */}}
        {{ if $eMethod.Returns }}return{{ end }}  this.wrapper{{ $eMethod.IfaceName }}.{{ $eMethod.Name }}(ctx, call{{ $eMethod.CallingArgs }});
    }
{{ end }} {{/* end range .EmbedMethods */}}

}
`

type serverWrapperMethod struct {
	CallingArgs     string
	CallingArgTypes []string
	DeclarationArgs string
	Doc             string
	IsStreaming     bool
	JavaDoc         string
	Name            string
	RecvType        string
	RetType         string
	RetJavaTypes    []string
	Returns         bool
	SendType        string
	Tags            []methodTag
}

type serverWrapperEmbedMethod struct {
	CallingArgs     string
	DeclarationArgs string
	Doc             string
	IfaceName       string
	JavaDoc         string
	Name            string
	RetType         string
	Returns         bool
}

type serverWrapperEmbed struct {
	Name     string
	FullName string
}

type methodTag struct {
	Value string
	Type  string
}

// TODO(sjr): move this to somewhere in util_*.
func toJavaString(goString string) string {
	result := strings.Replace(goString, "\"", "\\\"", -1)
	result = strings.Replace(result, "\n", "\" + \n\"", -1)
	return result
}

func processServerWrapperMethod(iface *compile.Interface, method *compile.Method, env *compile.Env, tags []methodTag) serverWrapperMethod {
	callArgTypes := make([]string, len(method.InArgs))
	for i, arg := range method.InArgs {
		callArgTypes[i] = javaReflectType(arg.Type, env)
	}
	retArgTypes := make([]string, len(method.OutArgs))
	for i, arg := range method.OutArgs {
		retArgTypes[i] = javaReflectType(arg.Type, env)
	}
	return serverWrapperMethod{
		CallingArgs:     javaCallingArgStr(method.InArgs, true),
		CallingArgTypes: callArgTypes,
		DeclarationArgs: javaDeclarationArgStr(method.InArgs, env, true),
		Doc:             toJavaString(method.Doc),
		IsStreaming:     isStreamingMethod(method),
		JavaDoc:         javaDoc(method.Doc, method.DocSuffix),
		Name:            vdlutil.FirstRuneToLower(method.Name),
		RecvType:        javaType(method.InStream, true, env),
		RetType:         serverInterfaceOutArg(iface, method, env),
		RetJavaTypes:    retArgTypes,
		Returns:         len(method.OutArgs) >= 1,
		SendType:        javaType(method.OutStream, true, env),
		Tags:            tags,
	}
}

func processServerWrapperEmbedMethod(iface *compile.Interface, embedMethod *compile.Method, env *compile.Env) serverWrapperEmbedMethod {
	return serverWrapperEmbedMethod{
		CallingArgs:     javaCallingArgStr(embedMethod.InArgs, true),
		DeclarationArgs: javaDeclarationArgStr(embedMethod.InArgs, env, true),
		IfaceName:       vdlutil.FirstRuneToUpper(iface.Name),
		JavaDoc:         javaDoc(embedMethod.Doc, embedMethod.DocSuffix),
		Name:            vdlutil.FirstRuneToLower(embedMethod.Name),
		RetType:         serverInterfaceOutArg(iface, embedMethod, env),
		Returns:         len(embedMethod.OutArgs) >= 1,
	}
}

// genJavaServerWrapperFile generates a java file containing a server wrapper for the specified
// interface.
func genJavaServerWrapperFile(iface *compile.Interface, env *compile.Env) JavaFileInfo {
	embeds := []serverWrapperEmbed{}
	for _, embed := range allEmbeddedIfaces(iface) {
		embeds = append(embeds, serverWrapperEmbed{
			Name:     vdlutil.FirstRuneToUpper(embed.Name),
			FullName: javaPath(javaGenPkgPath(path.Join(embed.File.Package.GenPath, vdlutil.FirstRuneToUpper(embed.Name)))),
		})
	}
	methodTags := make(map[string][]methodTag)
	// Copy method tags off of the interface.
	methods := make([]serverWrapperMethod, len(iface.Methods))
	for i, method := range iface.Methods {
		tags := make([]methodTag, len(method.Tags))
		for j, tag := range method.Tags {
			tags[j].Value = javaConstVal(tag, env)
			tags[j].Type = javaReflectType(tag.Type(), env)
		}
		methodTags[vdlutil.FirstRuneToLower(method.Name)] = tags
		methods[i] = processServerWrapperMethod(iface, method, env, tags)
	}
	embedMethods := []serverWrapperEmbedMethod{}
	for _, embedMao := range dedupedEmbeddedMethodAndOrigins(iface) {
		embedMethods = append(embedMethods, processServerWrapperEmbedMethod(embedMao.Origin, embedMao.Method, env))
	}
	javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
	data := struct {
		FileDoc         string
		EmbedMethods    []serverWrapperEmbedMethod
		Embeds          []serverWrapperEmbed
		FullServiceName string
		Methods         []serverWrapperMethod
		MethodTags      map[string][]methodTag
		PackagePath     string
		ServiceName     string
		Source          string
		Doc             string
	}{
		FileDoc:         iface.File.Package.FileDoc,
		EmbedMethods:    embedMethods,
		Embeds:          embeds,
		FullServiceName: javaPath(interfaceFullyQualifiedName(iface)),
		Methods:         methods,
		MethodTags:      methodTags,
		PackagePath:     javaPath(javaGenPkgPath(iface.File.Package.GenPath)),
		ServiceName:     javaServiceName,
		Source:          iface.File.BaseName,
		Doc:             toJavaString(iface.NamePos.Doc),
	}
	var buf bytes.Buffer
	err := parseTmpl("server wrapper", serverWrapperTmpl).Execute(&buf, data)
	if err != nil {
		log.Fatalf("vdl: couldn't execute server wrapper template: %v", err)
	}
	return JavaFileInfo{
		Name: javaServiceName + "ServerWrapper.java",
		Data: buf.Bytes(),
	}
}
<commit_msg>TBR v.io/x/ref: fix server-side streaming send<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package java

import (
	"bytes"
	"log"
	"path"
	"strings"

	"v.io/x/ref/lib/vdl/compile"
	"v.io/x/ref/lib/vdl/vdlutil"
)

const serverWrapperTmpl = header + `
// Source(s):  {{ .Source }}
package {{ .PackagePath }};

/**
 * Wrapper for {@link {{ .ServiceName }}Server}.  This wrapper is used by
 * {@link io.v.v23.rpc.ReflectInvoker} to indirectly invoke server methods.
 */
public final class {{ .ServiceName }}ServerWrapper {
    private final {{ .FullServiceName }}Server server;

{{/* Define fields to hold each of the embedded server wrappers*/}}
{{ range $embed := .Embeds }}
    {{/* e.g. private final com.somepackage.gen_impl.ArithStub stubArith; */}}
    private final {{ $embed.FullName }}ServerWrapper wrapper{{ $embed.Name }};
    {{ end }}

    /**
     * Creates a new {@link {{ .ServiceName }}ServerWrapper} to invoke the methods of the
     * provided server.
     *
     * @param server server whose methods are to be invoked
     */
    public {{ .ServiceName }}ServerWrapper({{ .FullServiceName }}Server server) {
        this.server = server;
        {{/* Initialize the embeded server wrappers */}}
        {{ range $embed := .Embeds }}
        this.wrapper{{ $embed.Name }} = new {{ $embed.FullName }}ServerWrapper(server);
        {{ end }}
    }

    /**
     * Returns a description of this server.
     */
    public io.v.v23.vdlroot.signature.Interface signature() {
        java.util.List<io.v.v23.vdlroot.signature.Embed> embeds = new java.util.ArrayList<io.v.v23.vdlroot.signature.Embed>();
        java.util.List<io.v.v23.vdlroot.signature.Method> methods = new java.util.ArrayList<io.v.v23.vdlroot.signature.Method>();
        {{ range $method := .Methods }}
        {
            java.util.List<io.v.v23.vdlroot.signature.Arg> inArgs = new java.util.ArrayList<io.v.v23.vdlroot.signature.Arg>();
            {{ range $arg := $method.CallingArgTypes }}
            inArgs.add(new io.v.v23.vdlroot.signature.Arg("", "", new io.v.v23.vdl.VdlTypeObject({{ $arg }})));
            {{ end }}
            java.util.List<io.v.v23.vdlroot.signature.Arg> outArgs = new java.util.ArrayList<io.v.v23.vdlroot.signature.Arg>();
            {{ range $arg := $method.RetJavaTypes }}
            outArgs.add(new io.v.v23.vdlroot.signature.Arg("", "", new io.v.v23.vdl.VdlTypeObject({{ $arg }})));
            {{ end }}
            java.util.List<io.v.v23.vdl.VdlAny> tags = new java.util.ArrayList<io.v.v23.vdl.VdlAny>();
            {{ range $tag := .Tags }}
            tags.add(new io.v.v23.vdl.VdlAny(io.v.v23.vdl.VdlValue.valueOf({{ $tag.Value }}, {{ $tag.Type }})));
            {{ end }}
            methods.add(new io.v.v23.vdlroot.signature.Method(
                "{{ $method.Name }}",
                "{{ $method.Doc }}",
                inArgs,
                outArgs,
                null,
                null,
                tags));
        }
        {{ end }}

        return new io.v.v23.vdlroot.signature.Interface("{{ .ServiceName }}", "{{ .PackagePath }}", "{{ .Doc }}", embeds, methods);
    }

    /**
     * Returns all tags associated with the provided method or {@code null} if the method isn't
     * implemented by this server.
     *
     * @param method method whose tags are to be returned
     */
    @SuppressWarnings("unused")
    public io.v.v23.vdl.VdlValue[] getMethodTags(java.lang.String method) throws io.v.v23.verror.VException {
        {{ range $methodName, $tags := .MethodTags }}
        if ("{{ $methodName }}".equals(method)) {
            try {
                return new io.v.v23.vdl.VdlValue[] {
                    {{ range $tag := $tags }} io.v.v23.vdl.VdlValue.valueOf({{ $tag.Value }}, {{ $tag.Type }}), {{ end }}
                };
            } catch (IllegalArgumentException e) {
                throw new io.v.v23.verror.VException(String.format("Couldn't get tags for method \"{{ $methodName }}\": %s", e.getMessage()));
            }
        }
        {{ end }}
        {{ range $embed := .Embeds }}
        {
            io.v.v23.vdl.VdlValue[] tags = this.wrapper{{ $embed.Name }}.getMethodTags(method);
            if (tags != null) {
                return tags;
            }
        }
        {{ end }}
        return null;  // method not found
    }

     {{/* Iterate over methods defined directly in the body of this server */}}
    {{ range $method := .Methods }}
    {{ $method.JavaDoc }}
    public {{ $method.RetType }} {{ $method.Name }}(io.v.v23.context.VContext _ctx, final io.v.v23.rpc.StreamServerCall _call{{ $method.DeclarationArgs }}) throws io.v.v23.verror.VException {
        {{ if $method.IsStreaming }}
        final io.v.v23.rpc.StreamIterable<{{ $method.RecvType }}> _it = new io.v.v23.rpc.StreamIterable(_call,new com.google.common.reflect.TypeToken<{{ $method.RecvType }}>() {}.getType());
        io.v.v23.vdl.ServerStream<{{ $method.SendType }}, {{ $method.RecvType }}> _stream = new io.v.v23.vdl.ServerStream<{{ $method.SendType }}, {{ $method.RecvType }}>() {
            @Override
            public void send({{ $method.SendType }} item) throws io.v.v23.verror.VException {
                java.lang.reflect.Type type = new com.google.common.reflect.TypeToken< {{ $method.SendType }} >() {}.getType();
                io.v.v23.VFutures.sync(_call.send(item, type));
            }
            @Override
            public java.util.Iterator<{{ $method.RecvType }}> iterator() {
                return _it.iterator();
            }
            @Override
            public io.v.v23.verror.VException error() {
                return _it.error();
            }
        };
        {{ end }} {{/* end if $method.IsStreaming */}}
        {{ if $method.Returns }} return {{ end }} this.server.{{ $method.Name }}(_ctx, _call {{ $method.CallingArgs }} {{ if $method.IsStreaming }} ,_stream {{ end }} );
    }
{{end}}

{{/* Iterate over methods from embeded servers and generate code to delegate the work */}}
{{ range $eMethod := .EmbedMethods }}
    {{ $eMethod.JavaDoc }}
    public {{ $eMethod.RetType }} {{ $eMethod.Name }}(io.v.v23.context.VContext ctx, io.v.v23.rpc.StreamServerCall call{{ $eMethod.DeclarationArgs }}) throws io.v.v23.verror.VException {
        {{/* e.g. return this.stubArith.cosine(ctx, call, [args], options) */}}
        {{ if $eMethod.Returns }}return{{ end }}  this.wrapper{{ $eMethod.IfaceName }}.{{ $eMethod.Name }}(ctx, call{{ $eMethod.CallingArgs }});
    }
{{ end }} {{/* end range .EmbedMethods */}}

}
`

type serverWrapperMethod struct {
	CallingArgs     string
	CallingArgTypes []string
	DeclarationArgs string
	Doc             string
	IsStreaming     bool
	JavaDoc         string
	Name            string
	RecvType        string
	RetType         string
	RetJavaTypes    []string
	Returns         bool
	SendType        string
	Tags            []methodTag
}

type serverWrapperEmbedMethod struct {
	CallingArgs     string
	DeclarationArgs string
	Doc             string
	IfaceName       string
	JavaDoc         string
	Name            string
	RetType         string
	Returns         bool
}

type serverWrapperEmbed struct {
	Name     string
	FullName string
}

type methodTag struct {
	Value string
	Type  string
}

// TODO(sjr): move this to somewhere in util_*.
func toJavaString(goString string) string {
	result := strings.Replace(goString, "\"", "\\\"", -1)
	result = strings.Replace(result, "\n", "\" + \n\"", -1)
	return result
}

func processServerWrapperMethod(iface *compile.Interface, method *compile.Method, env *compile.Env, tags []methodTag) serverWrapperMethod {
	callArgTypes := make([]string, len(method.InArgs))
	for i, arg := range method.InArgs {
		callArgTypes[i] = javaReflectType(arg.Type, env)
	}
	retArgTypes := make([]string, len(method.OutArgs))
	for i, arg := range method.OutArgs {
		retArgTypes[i] = javaReflectType(arg.Type, env)
	}
	return serverWrapperMethod{
		CallingArgs:     javaCallingArgStr(method.InArgs, true),
		CallingArgTypes: callArgTypes,
		DeclarationArgs: javaDeclarationArgStr(method.InArgs, env, true),
		Doc:             toJavaString(method.Doc),
		IsStreaming:     isStreamingMethod(method),
		JavaDoc:         javaDoc(method.Doc, method.DocSuffix),
		Name:            vdlutil.FirstRuneToLower(method.Name),
		RecvType:        javaType(method.InStream, true, env),
		RetType:         serverInterfaceOutArg(iface, method, env),
		RetJavaTypes:    retArgTypes,
		Returns:         len(method.OutArgs) >= 1,
		SendType:        javaType(method.OutStream, true, env),
		Tags:            tags,
	}
}

func processServerWrapperEmbedMethod(iface *compile.Interface, embedMethod *compile.Method, env *compile.Env) serverWrapperEmbedMethod {
	return serverWrapperEmbedMethod{
		CallingArgs:     javaCallingArgStr(embedMethod.InArgs, true),
		DeclarationArgs: javaDeclarationArgStr(embedMethod.InArgs, env, true),
		IfaceName:       vdlutil.FirstRuneToUpper(iface.Name),
		JavaDoc:         javaDoc(embedMethod.Doc, embedMethod.DocSuffix),
		Name:            vdlutil.FirstRuneToLower(embedMethod.Name),
		RetType:         serverInterfaceOutArg(iface, embedMethod, env),
		Returns:         len(embedMethod.OutArgs) >= 1,
	}
}

// genJavaServerWrapperFile generates a java file containing a server wrapper for the specified
// interface.
func genJavaServerWrapperFile(iface *compile.Interface, env *compile.Env) JavaFileInfo {
	embeds := []serverWrapperEmbed{}
	for _, embed := range allEmbeddedIfaces(iface) {
		embeds = append(embeds, serverWrapperEmbed{
			Name:     vdlutil.FirstRuneToUpper(embed.Name),
			FullName: javaPath(javaGenPkgPath(path.Join(embed.File.Package.GenPath, vdlutil.FirstRuneToUpper(embed.Name)))),
		})
	}
	methodTags := make(map[string][]methodTag)
	// Copy method tags off of the interface.
	methods := make([]serverWrapperMethod, len(iface.Methods))
	for i, method := range iface.Methods {
		tags := make([]methodTag, len(method.Tags))
		for j, tag := range method.Tags {
			tags[j].Value = javaConstVal(tag, env)
			tags[j].Type = javaReflectType(tag.Type(), env)
		}
		methodTags[vdlutil.FirstRuneToLower(method.Name)] = tags
		methods[i] = processServerWrapperMethod(iface, method, env, tags)
	}
	embedMethods := []serverWrapperEmbedMethod{}
	for _, embedMao := range dedupedEmbeddedMethodAndOrigins(iface) {
		embedMethods = append(embedMethods, processServerWrapperEmbedMethod(embedMao.Origin, embedMao.Method, env))
	}
	javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
	data := struct {
		FileDoc         string
		EmbedMethods    []serverWrapperEmbedMethod
		Embeds          []serverWrapperEmbed
		FullServiceName string
		Methods         []serverWrapperMethod
		MethodTags      map[string][]methodTag
		PackagePath     string
		ServiceName     string
		Source          string
		Doc             string
	}{
		FileDoc:         iface.File.Package.FileDoc,
		EmbedMethods:    embedMethods,
		Embeds:          embeds,
		FullServiceName: javaPath(interfaceFullyQualifiedName(iface)),
		Methods:         methods,
		MethodTags:      methodTags,
		PackagePath:     javaPath(javaGenPkgPath(iface.File.Package.GenPath)),
		ServiceName:     javaServiceName,
		Source:          iface.File.BaseName,
		Doc:             toJavaString(iface.NamePos.Doc),
	}
	var buf bytes.Buffer
	err := parseTmpl("server wrapper", serverWrapperTmpl).Execute(&buf, data)
	if err != nil {
		log.Fatalf("vdl: couldn't execute server wrapper template: %v", err)
	}
	return JavaFileInfo{
		Name: javaServiceName + "ServerWrapper.java",
		Data: buf.Bytes(),
	}
}
<|endoftext|>


<commit_before>package provision

import (
	"bytes"
	"errors"
	"fmt"
	"regexp"
	"text/template"

	"github.com/docker/machine/libmachine/auth"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/engine"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/provision/pkgaction"
	"github.com/docker/machine/libmachine/provision/serviceaction"
	"github.com/docker/machine/libmachine/swarm"
)

var (
	ErrUnknownYumOsRelease = errors.New("unknown OS for Yum repository")

	packageListTemplate = `[docker]
name=Docker Stable Repository
baseurl=https://yum.dockerproject.org/repo/main/{{.OsRelease}}/{{.OsReleaseVersion}}
priority=1
enabled=1
gpgkey=https://yum.dockerproject.org/gpg
`
	engineConfigTemplate = `[Unit]
Description=Docker Application Container Engine
After=network.target docker.socket
Requires=docker.socket

[Service]
ExecStart=/usr/bin/docker daemon -H tcp://0.0.0.0:{{.DockerPort}} -H unix:///var/run/docker.sock --storage-driver {{.EngineOptions.StorageDriver}} --tlsverify --tlscacert {{.AuthOptions.CaCertRemotePath}} --tlscert {{.AuthOptions.ServerCertRemotePath}} --tlskey {{.AuthOptions.ServerKeyRemotePath}} {{ range .EngineOptions.Labels }}--label {{.}} {{ end }}{{ range .EngineOptions.InsecureRegistry }}--insecure-registry {{.}} {{ end }}{{ range .EngineOptions.RegistryMirror }}--registry-mirror {{.}} {{ end }}{{ range .EngineOptions.ArbitraryFlags }}--{{.}} {{ end }}
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
Environment={{range .EngineOptions.Env}}{{ printf "%q" . }} {{end}}
`

	majorVersionRE = regexp.MustCompile(`^(\d+)(\..*)?`)
)

type PackageListInfo struct {
	OsRelease        string
	OsReleaseVersion string
}

func init() {
	Register("RedHat", &RegisteredProvisioner{
		New: func(d drivers.Driver) Provisioner {
			return NewRedHatProvisioner("rhel", d)
		},
	})
}

func NewRedHatProvisioner(osReleaseID string, d drivers.Driver) *RedHatProvisioner {
	systemdProvisioner := NewSystemdProvisioner(osReleaseID, d)
	systemdProvisioner.SSHCommander = RedHatSSHCommander{Driver: d}
	return &RedHatProvisioner{
		systemdProvisioner,
	}
}

type RedHatProvisioner struct {
	SystemdProvisioner
}

func (provisioner *RedHatProvisioner) String() string {
	return "redhat"
}

func (provisioner *RedHatProvisioner) SetHostname(hostname string) error {
	// we have to have SetHostname here as well to use the RedHat provisioner
	// SSHCommand to add the tty allocation
	if _, err := provisioner.SSHCommand(fmt.Sprintf(
		"sudo hostname %s && echo %q | sudo tee /etc/hostname",
		hostname,
		hostname,
	)); err != nil {
		return err
	}

	// ubuntu/debian use 127.0.1.1 for non "localhost" loopback hostnames: https://www.debian.org/doc/manuals/debian-reference/ch05.en.html#_the_hostname_resolution
	if _, err := provisioner.SSHCommand(fmt.Sprintf(
		"if grep -xq 127.0.1.1.* /etc/hosts; then sudo sed -i 's/^127.0.1.1.*/127.0.1.1 %s/g' /etc/hosts; else echo '127.0.1.1 %s' | sudo tee -a /etc/hosts; fi",
		hostname,
		hostname,
	)); err != nil {
		return err
	}

	return nil
}

func (provisioner *RedHatProvisioner) Package(name string, action pkgaction.PackageAction) error {
	var packageAction string

	switch action {
	case pkgaction.Install:
		packageAction = "install"
	case pkgaction.Remove:
		packageAction = "remove"
	case pkgaction.Upgrade:
		packageAction = "upgrade"
	}

	command := fmt.Sprintf("sudo -E yum %s -y %s", packageAction, name)

	if _, err := provisioner.SSHCommand(command); err != nil {
		return err
	}

	return nil
}

func installDocker(provisioner *RedHatProvisioner) error {
	if err := installDockerGeneric(provisioner, provisioner.EngineOptions.InstallURL); err != nil {
		return err
	}

	if err := provisioner.Service("docker", serviceaction.Restart); err != nil {
		return err
	}

	if err := provisioner.Service("docker", serviceaction.Enable); err != nil {
		return err
	}

	return nil
}

func (provisioner *RedHatProvisioner) dockerDaemonResponding() bool {
	log.Debug("checking docker daemon")

	if out, err := provisioner.SSHCommand("sudo docker version"); err != nil {
		log.Warnf("Error getting SSH command to check if the daemon is up: %s", err)
		log.Debugf("'sudo docker version' output:\n%s", out)
		return false
	}

	// The daemon is up if the command worked.  Carry on.
	return true
}

func (provisioner *RedHatProvisioner) Provision(swarmOptions swarm.Options, authOptions auth.Options, engineOptions engine.Options) error {
	provisioner.SwarmOptions = swarmOptions
	provisioner.AuthOptions = authOptions
	provisioner.EngineOptions = engineOptions
	swarmOptions.Env = engineOptions.Env

	// set default storage driver for redhat
	storageDriver, err := decideStorageDriver(provisioner, "devicemapper", engineOptions.StorageDriver)
	if err != nil {
		return err
	}
	provisioner.EngineOptions.StorageDriver = storageDriver

	if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil {
		return err
	}

	for _, pkg := range provisioner.Packages {
		log.Debugf("installing base package: name=%s", pkg)
		if err := provisioner.Package(pkg, pkgaction.Install); err != nil {
			return err
		}
	}

	// update OS -- this is needed for libdevicemapper and the docker install
	if _, err := provisioner.SSHCommand("sudo -E yum -y update"); err != nil {
		return err
	}

	// install docker
	if err := installDocker(provisioner); err != nil {
		return err
	}

	if err := mcnutils.WaitFor(provisioner.dockerDaemonResponding); err != nil {
		return err
	}

	if err := makeDockerOptionsDir(provisioner); err != nil {
		return err
	}

	provisioner.AuthOptions = setRemoteAuthOptions(provisioner)

	if err := ConfigureAuth(provisioner); err != nil {
		return err
	}

	if err := configureSwarm(provisioner, swarmOptions, provisioner.AuthOptions); err != nil {
		return err
	}

	return nil
}

func (provisioner *RedHatProvisioner) GenerateDockerOptions(dockerPort int) (*DockerOptions, error) {
	var (
		engineCfg  bytes.Buffer
		configPath = provisioner.DaemonOptionsFile
	)

	driverNameLabel := fmt.Sprintf("provider=%s", provisioner.Driver.DriverName())
	provisioner.EngineOptions.Labels = append(provisioner.EngineOptions.Labels, driverNameLabel)

	// systemd / redhat will not load options if they are on newlines
	// instead, it just continues with a different set of options; yeah...
	t, err := template.New("engineConfig").Parse(engineConfigTemplate)
	if err != nil {
		return nil, err
	}

	engineConfigContext := EngineConfigContext{
		DockerPort:       dockerPort,
		AuthOptions:      provisioner.AuthOptions,
		EngineOptions:    provisioner.EngineOptions,
		DockerOptionsDir: provisioner.DockerOptionsDir,
	}

	t.Execute(&engineCfg, engineConfigContext)

	daemonOptsDir := configPath
	return &DockerOptions{
		EngineOptions:     engineCfg.String(),
		EngineOptionsPath: daemonOptsDir,
	}, nil
}

func generateYumRepoList(provisioner Provisioner) (*bytes.Buffer, error) {
	packageListInfo := &PackageListInfo{}

	releaseInfo, err := provisioner.GetOsReleaseInfo()
	if err != nil {
		return nil, err
	}

	switch releaseInfo.ID {
	case "rhel", "centos":
		// rhel and centos both use the "centos" repo
		packageListInfo.OsRelease = "centos"
		packageListInfo.OsReleaseVersion = "7"
	case "fedora":
		packageListInfo.OsRelease = "fedora"
		packageListInfo.OsReleaseVersion = "23"
	case "ol":
		packageListInfo.OsRelease = "oraclelinux"
		v := majorVersionRE.FindStringSubmatch(releaseInfo.Version)
		if v == nil {
			return nil, fmt.Errorf("unable to determine major version of %s", releaseInfo.Version)
		}
		packageListInfo.OsReleaseVersion = v[1]
	default:
		return nil, ErrUnknownYumOsRelease
	}

	t, err := template.New("packageList").Parse(packageListTemplate)
	if err != nil {
		return nil, err
	}

	var buf bytes.Buffer

	if err := t.Execute(&buf, packageListInfo); err != nil {
		return nil, err
	}

	return &buf, nil
}

func (provisioner *RedHatProvisioner) ConfigurePackageList() error {
	buf, err := generateYumRepoList(provisioner)
	if err != nil {
		return err
	}

	// we cannot use %q here as it combines the newlines in the formatting
	// on transport causing yum to not use the repo
	packageCmd := fmt.Sprintf("echo \"%s\" | sudo tee /etc/yum.repos.d/docker.repo", buf.String())
	if _, err := provisioner.SSHCommand(packageCmd); err != nil {
		return err
	}

	return nil
}
<commit_msg>Update redhat.go<commit_after>package provision

import (
	"bytes"
	"errors"
	"fmt"
	"regexp"
	"text/template"

	"github.com/docker/machine/libmachine/auth"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/engine"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/provision/pkgaction"
	"github.com/docker/machine/libmachine/provision/serviceaction"
	"github.com/docker/machine/libmachine/swarm"
)

var (
	ErrUnknownYumOsRelease = errors.New("unknown OS for Yum repository")
	doc_version=""

	packageListTemplate = `[docker]
name=Docker Stable Repository
baseurl=https://yum.dockerproject.org/repo/main/{{.OsRelease}}/{{.OsReleaseVersion}}
priority=1
enabled=1
gpgkey=https://yum.dockerproject.org/gpg
`
	engineConfigTemplate = `[Unit]
Description=Docker Application Container Engine
After=network.target docker.socket
Requires=docker.socket

[Service]
ExecStart=/usr/bin/docker daemon -H tcp://0.0.0.0:{{.DockerPort}} -H unix:///var/run/docker.sock --storage-driver {{.EngineOptions.StorageDriver}} --tlsverify --tlscacert {{.AuthOptions.CaCertRemotePath}} --tlscert {{.AuthOptions.ServerCertRemotePath}} --tlskey {{.AuthOptions.ServerKeyRemotePath}} {{ range .EngineOptions.Labels }}--label {{.}} {{ end }}{{ range .EngineOptions.InsecureRegistry }}--insecure-registry {{.}} {{ end }}{{ range .EngineOptions.RegistryMirror }}--registry-mirror {{.}} {{ end }}{{ range .EngineOptions.ArbitraryFlags }}--{{.}} {{ end }}
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
Environment={{range .EngineOptions.Env}}{{ printf "%q" . }} {{end}}
`

	majorVersionRE = regexp.MustCompile(`^(\d+)(\..*)?`)
)

type PackageListInfo struct {
	OsRelease        string
	OsReleaseVersion string
}

func init() {
	Register("RedHat", &RegisteredProvisioner{
		New: func(d drivers.Driver) Provisioner {
			return NewRedHatProvisioner("rhel", d)
		},
	})
}

func NewRedHatProvisioner(osReleaseID string, d drivers.Driver) *RedHatProvisioner {
	systemdProvisioner := NewSystemdProvisioner(osReleaseID, d)
	systemdProvisioner.SSHCommander = RedHatSSHCommander{Driver: d}
	return &RedHatProvisioner{
		systemdProvisioner,
	}
}

type RedHatProvisioner struct {
	SystemdProvisioner
}

func (provisioner *RedHatProvisioner) String() string {
	return "redhat"
}

func dockerVersion(docker_version string){
	doc_version=docker_version
}

func (provisioner *RedHatProvisioner) SetHostname(hostname string) error {
	// we have to have SetHostname here as well to use the RedHat provisioner
	// SSHCommand to add the tty allocation
	if _, err := provisioner.SSHCommand(fmt.Sprintf(
		"sudo hostname %s && echo %q | sudo tee /etc/hostname",
		hostname,
		hostname,
	)); err != nil {
		return err
	}

	// ubuntu/debian use 127.0.1.1 for non "localhost" loopback hostnames: https://www.debian.org/doc/manuals/debian-reference/ch05.en.html#_the_hostname_resolution
	if _, err := provisioner.SSHCommand(fmt.Sprintf(
		"if grep -xq 127.0.1.1.* /etc/hosts; then sudo sed -i 's/^127.0.1.1.*/127.0.1.1 %s/g' /etc/hosts; else echo '127.0.1.1 %s' | sudo tee -a /etc/hosts; fi",
		hostname,
		hostname,
	)); err != nil {
		return err
	}

	return nil
}

func (provisioner *RedHatProvisioner) Package(name string, action pkgaction.PackageAction) error {
	var packageAction string

	switch action {
	case pkgaction.Install:
		packageAction = "install"
	case pkgaction.Remove:
		packageAction = "remove"
	case pkgaction.Upgrade:
		packageAction = "upgrade"
	}

	command := fmt.Sprintf("sudo -E yum %s -y %s", packageAction, name)

	if _, err := provisioner.SSHCommand(command); err != nil {
		return err
	}

	return nil
}

func installDocker(provisioner *RedHatProvisioner) error {
	if err := installDockerGeneric(provisioner, provisioner.EngineOptions.InstallURL); err != nil {
		return err
	}

	if err := provisioner.Service("docker", serviceaction.Restart); err != nil {
		return err
	}

	if err := provisioner.Service("docker", serviceaction.Enable); err != nil {
		return err
	}

	return nil
}

func (provisioner *RedHatProvisioner) dockerDaemonResponding() bool {
	log.Debug("checking docker daemon")

	if out, err := provisioner.SSHCommand("sudo docker version"); err != nil {
		log.Warnf("Error getting SSH command to check if the daemon is up: %s", err)
		log.Debugf("'sudo docker version' output:\n%s", out)
		return false
	}

	// The daemon is up if the command worked.  Carry on.
	return true
}

func (provisioner *RedHatProvisioner) Provision(swarmOptions swarm.Options, authOptions auth.Options, engineOptions engine.Options) error {
	provisioner.SwarmOptions = swarmOptions
	provisioner.AuthOptions = authOptions
	provisioner.EngineOptions = engineOptions
	swarmOptions.Env = engineOptions.Env

	// set default storage driver for redhat
	storageDriver, err := decideStorageDriver(provisioner, "devicemapper", engineOptions.StorageDriver)
	if err != nil {
		return err
	}
	provisioner.EngineOptions.StorageDriver = storageDriver

	if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil {
		return err
	}

	for _, pkg := range provisioner.Packages {
		log.Debugf("installing base package: name=%s", pkg)
		if err := provisioner.Package(pkg, pkgaction.Install doc_version); err != nil {
			return err
		}
	}

	// update OS -- this is needed for libdevicemapper and the docker install
	if _, err := provisioner.SSHCommand("sudo -E yum -y update"); err != nil {
		return err
	}

	// install docker
	if err := installDocker(provisioner); err != nil {
		return err
	}

	if err := mcnutils.WaitFor(provisioner.dockerDaemonResponding); err != nil {
		return err
	}

	if err := makeDockerOptionsDir(provisioner); err != nil {
		return err
	}

	provisioner.AuthOptions = setRemoteAuthOptions(provisioner)

	if err := ConfigureAuth(provisioner); err != nil {
		return err
	}

	if err := configureSwarm(provisioner, swarmOptions, provisioner.AuthOptions); err != nil {
		return err
	}

	return nil
}

func (provisioner *RedHatProvisioner) GenerateDockerOptions(dockerPort int) (*DockerOptions, error) {
	var (
		engineCfg  bytes.Buffer
		configPath = provisioner.DaemonOptionsFile
	)

	driverNameLabel := fmt.Sprintf("provider=%s", provisioner.Driver.DriverName())
	provisioner.EngineOptions.Labels = append(provisioner.EngineOptions.Labels, driverNameLabel)

	// systemd / redhat will not load options if they are on newlines
	// instead, it just continues with a different set of options; yeah...
	t, err := template.New("engineConfig").Parse(engineConfigTemplate)
	if err != nil {
		return nil, err
	}

	engineConfigContext := EngineConfigContext{
		DockerPort:       dockerPort,
		AuthOptions:      provisioner.AuthOptions,
		EngineOptions:    provisioner.EngineOptions,
		DockerOptionsDir: provisioner.DockerOptionsDir,
	}

	t.Execute(&engineCfg, engineConfigContext)

	daemonOptsDir := configPath
	return &DockerOptions{
		EngineOptions:     engineCfg.String(),
		EngineOptionsPath: daemonOptsDir,
	}, nil
}

func generateYumRepoList(provisioner Provisioner) (*bytes.Buffer, error) {
	packageListInfo := &PackageListInfo{}

	releaseInfo, err := provisioner.GetOsReleaseInfo()
	if err != nil {
		return nil, err
	}

	switch releaseInfo.ID {
	case "rhel", "centos":
		// rhel and centos both use the "centos" repo
		packageListInfo.OsRelease = "centos"
		packageListInfo.OsReleaseVersion = "7"
	case "fedora":
		packageListInfo.OsRelease = "fedora"
		packageListInfo.OsReleaseVersion = "23"
	case "ol":
		packageListInfo.OsRelease = "oraclelinux"
		v := majorVersionRE.FindStringSubmatch(releaseInfo.Version)
		if v == nil {
			return nil, fmt.Errorf("unable to determine major version of %s", releaseInfo.Version)
		}
		packageListInfo.OsReleaseVersion = v[1]
	default:
		return nil, ErrUnknownYumOsRelease
	}

	t, err := template.New("packageList").Parse(packageListTemplate)
	if err != nil {
		return nil, err
	}

	var buf bytes.Buffer

	if err := t.Execute(&buf, packageListInfo); err != nil {
		return nil, err
	}

	return &buf, nil
}

func (provisioner *RedHatProvisioner) ConfigurePackageList() error {
	buf, err := generateYumRepoList(provisioner)
	if err != nil {
		return err
	}

	// we cannot use %q here as it combines the newlines in the formatting
	// on transport causing yum to not use the repo
	packageCmd := fmt.Sprintf("echo \"%s\" | sudo tee /etc/yum.repos.d/docker.repo", buf.String())
	if _, err := provisioner.SSHCommand(packageCmd); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package rpmpack

import (
	"fmt"
	"regexp"
)

type rpmSense uint32

// SenseAny specifies no specific version compare
// SenseLess specifies less then the specified version
// SenseGreater specifies greater then the specified version
// SenseEqual specifies equal to the specified version
const (
	SenseAny rpmSense = 1 << iota >> 1
	SenseLess
	SenseGreater
	SenseEqual
)

var relationMatch = regexp.MustCompile(`([^=<>\s]*)\s*((?:=|>|<)*)\s*(.*)?`)

// Relation is the structure of rpm sense relationships
type Relation struct {
	Name    string
	Version string
	Sense   rpmSense
}

// String return the string representation of the Relation
func (r *Relation) String() string {
	return fmt.Sprintf("%s%v%s", r.Name, r.Sense, r.Version)
}

// Equal compare the equality of two relations
func (r *Relation) Equal(o *Relation) bool {
	return r.Name == o.Name && r.Version == o.Version && r.Sense == o.Sense
}

// Relations is a slice of Relation pointers
type Relations []*Relation

// String return the string representation of the Relations
func (r *Relations) String() string {
	var (
		val   string
		total = len(*r)
	)

	for idx, relation := range *r {
		val += relation.String()
		if idx < total-1 {
			val += ","
		}
	}

	return val
}

// Set parse a string into a Relation and append it to the Relations slice if it is missing
// this is used by the flag package
func (r *Relations) Set(value string) error {
	relation, err := NewRelation(value)
	if err != nil {
		return err
	}
	r.addIfMissing(relation)

	return nil
}

func (r *Relations) addIfMissing(value *Relation) {
	for _, relation := range *r {
		if relation.Equal(value) {
			return
		}
	}

	*r = append(*r, value)
}

// AddToIndex add the relations to the specified category on the index
func (r *Relations) AddToIndex(h *index, nameTag, versionTag, flagsTag int) error {
	var (
		num      = len(*r)
		names    = make([]string, num)
		versions = make([]string, num)
		flags    = make([]uint32, num)
	)

	if num == 0 {
		return nil
	}

	for idx := range *r {
		relation := (*r)[idx]
		names[idx] = relation.Name
		versions[idx] = relation.Version
		flags[idx] = uint32(relation.Sense)
	}

	h.Add(nameTag, entry(names))
	h.Add(versionTag, entry(versions))
	h.Add(flagsTag, entry(flags))

	return nil
}

// NewRelation parse a string into a Relation
func NewRelation(related string) (*Relation, error) {
	var (
		err   error
		sense rpmSense
	)
	parts := relationMatch.FindStringSubmatch(related)
	if sense, err = parseSense(parts[2]); err != nil {
		return nil, err
	}

	return &Relation{
		Name:    parts[1],
		Version: parts[3],
		Sense:   sense,
	}, nil
}

var stringToSense = map[string]rpmSense{
	"":   SenseAny,
	"<":  SenseLess,
	">":  SenseGreater,
	"=":  SenseEqual,
	"<=": SenseLess | SenseEqual,
	">=": SenseGreater | SenseEqual,
}

// String return the string representation of the rpmSense
func (r rpmSense) String() string {
	var (
		val rpmSense
		ret string
	)

	for ret, val = range stringToSense {
		if r == val {
			return ret
		}
	}

	return "unknown"
}

func parseSense(sense string) (rpmSense, error) {
	var (
		ret rpmSense
		ok  bool
	)
	if ret, ok = stringToSense[sense]; !ok {
		return SenseAny, fmt.Errorf("unknown sense value: %s", sense)
	}

	return ret, nil
}
<commit_msg>clean up the String function for relations<commit_after>package rpmpack

import (
	"fmt"
	"regexp"
	"strings"
)

type rpmSense uint32

// SenseAny specifies no specific version compare
// SenseLess specifies less then the specified version
// SenseGreater specifies greater then the specified version
// SenseEqual specifies equal to the specified version
const (
	SenseAny rpmSense = 1 << iota >> 1
	SenseLess
	SenseGreater
	SenseEqual
)

var relationMatch = regexp.MustCompile(`([^=<>\s]*)\s*((?:=|>|<)*)\s*(.*)?`)

// Relation is the structure of rpm sense relationships
type Relation struct {
	Name    string
	Version string
	Sense   rpmSense
}

// String return the string representation of the Relation
func (r *Relation) String() string {
	return fmt.Sprintf("%s%v%s", r.Name, r.Sense, r.Version)
}

// Equal compare the equality of two relations
func (r *Relation) Equal(o *Relation) bool {
	return r.Name == o.Name && r.Version == o.Version && r.Sense == o.Sense
}

// Relations is a slice of Relation pointers
type Relations []*Relation

// String return the string representation of the Relations
func (r *Relations) String() string {
	var val []string
	for _, rel := range *r {
		val = append(val, rel.String())
	}
	return strings.Join(val, ",")
}

// Set parse a string into a Relation and append it to the Relations slice if it is missing
// this is used by the flag package
func (r *Relations) Set(value string) error {
	relation, err := NewRelation(value)
	if err != nil {
		return err
	}
	r.addIfMissing(relation)

	return nil
}

func (r *Relations) addIfMissing(value *Relation) {
	for _, relation := range *r {
		if relation.Equal(value) {
			return
		}
	}

	*r = append(*r, value)
}

// AddToIndex add the relations to the specified category on the index
func (r *Relations) AddToIndex(h *index, nameTag, versionTag, flagsTag int) error {
	var (
		num      = len(*r)
		names    = make([]string, num)
		versions = make([]string, num)
		flags    = make([]uint32, num)
	)

	if num == 0 {
		return nil
	}

	for idx := range *r {
		relation := (*r)[idx]
		names[idx] = relation.Name
		versions[idx] = relation.Version
		flags[idx] = uint32(relation.Sense)
	}

	h.Add(nameTag, entry(names))
	h.Add(versionTag, entry(versions))
	h.Add(flagsTag, entry(flags))

	return nil
}

// NewRelation parse a string into a Relation
func NewRelation(related string) (*Relation, error) {
	var (
		err   error
		sense rpmSense
	)
	parts := relationMatch.FindStringSubmatch(related)
	if sense, err = parseSense(parts[2]); err != nil {
		return nil, err
	}

	return &Relation{
		Name:    parts[1],
		Version: parts[3],
		Sense:   sense,
	}, nil
}

var stringToSense = map[string]rpmSense{
	"":   SenseAny,
	"<":  SenseLess,
	">":  SenseGreater,
	"=":  SenseEqual,
	"<=": SenseLess | SenseEqual,
	">=": SenseGreater | SenseEqual,
}

// String return the string representation of the rpmSense
func (r rpmSense) String() string {
	var (
		val rpmSense
		ret string
	)

	for ret, val = range stringToSense {
		if r == val {
			return ret
		}
	}

	return "unknown"
}

func parseSense(sense string) (rpmSense, error) {
	var (
		ret rpmSense
		ok  bool
	)
	if ret, ok = stringToSense[sense]; !ok {
		return SenseAny, fmt.Errorf("unknown sense value: %s", sense)
	}

	return ret, nil
}
<|endoftext|>


<commit_before>// written by Daniel Oaks <daniel@danieloaks.net>
// released under the ISC license

package main

import (
	"bufio"
	"database/sql"
	"encoding/base64"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"syscall"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/DanielOaks/gircbnc/ircbnc"
	"github.com/fatih/color"
)

var (
	cbBlue   = color.New(color.Bold, color.FgHiBlue).SprintfFunc()
	cbCyan   = color.New(color.Bold, color.FgHiCyan).SprintfFunc()
	cbYellow = color.New(color.Bold, color.FgHiYellow).SprintfFunc()
	cbRed    = color.New(color.Bold, color.FgHiRed).SprintfFunc()
)

// Section displays a section to the user
func Section(text string) {
	Note("")
	fmt.Println(cbBlue("["), cbYellow("**"), cbBlue("]"), "--", text, "--")
	Note("")
}

// Note displays a note to the user
func Note(text string) {
	fmt.Println(cbBlue("["), cbYellow("**"), cbBlue("]"), text)
}

// Query asks for a value from the user
func Query(prompt string) (string, error) {
	fmt.Print(cbBlue("[ "), cbYellow("??"), cbBlue(" ] "), prompt)

	in := bufio.NewReader(os.Stdin)
	response, err := in.ReadString('\n')
	return strings.TrimRight(response, "\r\n"), err
}

// QueryNoEcho asks for a value from the user without echoing what they type
func QueryNoEcho(prompt string) (string, error) {
	fmt.Print(cbBlue("[ "), cbYellow("??"), cbBlue(" ] "), prompt)

	response, err := terminal.ReadPassword(int(syscall.Stdin))
	fmt.Print("\n")
	return string(response), err
}

// QueryDefault asks for a value, falling back to a default
func QueryDefault(prompt string, defaultValue string) (string, error) {
	response, err := Query(prompt)

	if err != nil {
		return "", err
	}

	if len(strings.TrimSpace(response)) < 1 {
		return defaultValue, nil
	}
	return response, nil
}

// QueryBool asks for a true/false value from the user
func QueryBool(prompt string) (bool, error) {
	for {
		response, err := Query(prompt)
		if err != nil {
			return false, err
		}

		response = strings.ToLower(strings.TrimSpace(response))
		if len(response) < 1 {
			continue
		}

		// check for yes/true/1 or no/false/0
		if strings.Contains("yt1", string(response[0])) {
			return true, nil
		} else if strings.Contains("nf0", string(response[0])) {
			return false, nil
		}
	}
}

// Warn warns the user about something
func Warn(text string) {
	fmt.Println(cbBlue("["), cbRed("**"), cbBlue("]"), text)
}

// Error shows the user an error
func Error(text string) {
	fmt.Println(cbBlue("["), cbRed("!!"), cbBlue("]"), cbRed(text))
}

// InitialSetup performs the initial gircbnc setup
func InitialSetup(db *sql.DB) {
	fmt.Println(cbBlue("["), cbCyan("~~"), cbBlue("]"), "Welcome to", cbCyan("gIRCbnc"))
	Note("We will now run through basic setup.")

	var err error

	// generate the password salt used by the bouncer
	bncSalt, err := ircbnc.NewSalt()
	if err != nil {
		log.Fatal("Could not generate cryptographically-secure salt for the bouncer:", err.Error())
	}

	db.Exec(`INSERT INTO ircbnc (key, value) VALUES ("salt",?)`, base64.StdEncoding.EncodeToString(bncSalt))

	Section("Admin user settings")
	var username string
	var goodUsername string
	for {
		username, err = Query("Username: ")
		if err != nil {
			log.Fatal(err.Error())
		}

		username = strings.TrimSpace(username)

		goodUsername, err = ircbnc.BncName(username)
		if err == nil {
			Note(fmt.Sprintf("Username is %s. Will be stored internally as %s.", username, goodUsername))
			break
		} else {
			Error(err.Error())
		}
	}

	// generate our salts
	userSalt, err := ircbnc.NewSalt()
	if err != nil {
		log.Fatal("Could not generate cryptographically-secure salt for the user:", err.Error())
	}

	var passHash []byte
	for {
		password, err := QueryNoEcho("Enter password: ")

		if err != nil {
			Error(fmt.Sprintf("Error reading input line: %s", err.Error()))
			continue
		}

		passwordCompare, err := QueryNoEcho("Confirm password: ")

		if err != nil {
			Error(fmt.Sprintf("Error reading input line: %s", err.Error()))
			continue
		}

		if password != passwordCompare {
			Warn("The supplied passwords do not match")
			continue
		}

		passHash, err = ircbnc.GenerateFromPassword(bncSalt, userSalt, password)

		if err == nil {
			break
		} else {
			Error(fmt.Sprintf("Could not generate password: %s", err.Error()))
			continue
		}
	}

	// get IRC details
	var ircNick string
	for {
		ircNick, err = QueryDefault(fmt.Sprintf("Enter Nickname [%s]: ", goodUsername), goodUsername)
		if err != nil {
			log.Fatal(err.Error())
		}

		ircNick, err = ircbnc.IrcName(ircNick, false)
		if err == nil {
			break
		} else {
			Error(err.Error())
		}
	}

	var ircFbNick string
	defaultFallbackNick := fmt.Sprintf("%s_", ircNick)
	for {
		ircFbNick, err = QueryDefault(fmt.Sprintf("Enter Fallback Nickname [%s]: ", defaultFallbackNick), defaultFallbackNick)
		if err != nil {
			log.Fatal(err.Error())
		}

		ircFbNick, err = ircbnc.IrcName(ircFbNick, false)
		if err == nil {
			break
		} else {
			Error(err.Error())
		}
	}

	var ircUser string
	for {
		ircUser, err = QueryDefault(fmt.Sprintf("Enter Username [%s]: ", goodUsername), goodUsername)
		if err != nil {
			log.Fatal(err.Error())
		}

		ircUser, err = ircbnc.IrcName(ircUser, false)
		if err == nil {
			break
		} else {
			Error(err.Error())
		}
	}

	var ircReal string
	ircReal, err = Query("Enter Realname: ")
	if err != nil {
		log.Fatal(err.Error())
	}

	db.Exec(`INSERT INTO users (id, salt, password, default_nickname, default_fallback_nickname, default_username, default_realname) VALUES (?,?,?,?,?,?,?)`,
		goodUsername, userSalt, passHash, ircNick, ircFbNick, ircUser, ircReal)

	// now setup default networks for that user
	Section("Network Setup")

	for {
		setupNewNet, err := QueryBool("Set up a network? (y/n) ")
		if err != nil {
			log.Fatal(err.Error())
		}

		if !setupNewNet {
			break
		}

		var goodNetName string
		for {
			netName, err := Query("Name (e.g. freenode): ")
			if err != nil {
				log.Fatal(err.Error())
			}

			goodNetName, err = ircbnc.BncName(netName)
			if err == nil {
				Note(fmt.Sprintf("Network name is %s. Will be stored internally as %s.", netName, goodNetName))
				break
			} else {
				Error(err.Error())
			}
		}

		var serverAddress string
		for {
			serverAddress, err = Query("Server host (e.g. chat.freenode.net): ")
			if err != nil {
				log.Fatal(err.Error())
			}

			if len(strings.TrimSpace(serverAddress)) < 1 {
				Error("Hostname must have at least one character!")
				continue
			}

			break
		}

		serverUseTLS, err := QueryBool("Server uses SSL/TLS? (y/n) ")
		if err != nil {
			log.Fatal(err.Error())
		}

		var defaultPort int
		if serverUseTLS {
			defaultPort = 6697
		} else {
			defaultPort = 6667
		}

		var serverPort int
		for {
			portString, err := QueryDefault(fmt.Sprintf("Server Port [%s]: ", strconv.Itoa(defaultPort)), strconv.Itoa(defaultPort))
			if err != nil {
				log.Fatal(err.Error())
			}

			serverPort, err = strconv.Atoi(portString)
			if err != nil {
				Error(err.Error())
				continue
			}

			if (serverPort < 1) || (serverPort > 65535) {
				Error("Port number can be 1 - 65535")
				continue
			}

			break
		}

		serverPass, err := Query("Server connection password (probably empty): ")
		if err != nil {
			log.Fatal(err.Error())
		}

		var serverChannels []string
		for {
			serverChannelsString, err := Query("Channels to autojoin (separated by spaces): ")
			if err != nil {
				log.Fatal(err.Error())
			}

			serverChannels = make([]string, 0)

			for _, channel := range strings.Fields(serverChannelsString) {
				channel, err := ircbnc.IrcName(channel, true)
				if err != nil {
					break
				}

				serverChannels = append(serverChannels, channel)
			}

			if err != nil {
				Error(err.Error())
				continue
			}

			break
		}

		db.Exec(`INSERT INTO server_connections (user_id, name, password) VALUES (?,?,?)`,
			goodUsername, goodNetName, serverPass)
		db.Exec(`INSERT INTO server_connection_addresses (user_id, sc_name, address, port, use_tls) VALUES (?,?,?,?,?)`,
			goodUsername, goodNetName, serverAddress, serverPort, serverUseTLS)
		for _, channel := range serverChannels {
			db.Exec(`INSERT INTO server_connection_channels (user_id, sc_name, name) VALUES (?,?,?)`,
				goodUsername, goodNetName, channel)
		}
	}
}
<commit_msg>setup: Add small post-setup message<commit_after>// written by Daniel Oaks <daniel@danieloaks.net>
// released under the ISC license

package main

import (
	"bufio"
	"database/sql"
	"encoding/base64"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"syscall"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/DanielOaks/gircbnc/ircbnc"
	"github.com/fatih/color"
)

var (
	cbBlue   = color.New(color.Bold, color.FgHiBlue).SprintfFunc()
	cbCyan   = color.New(color.Bold, color.FgHiCyan).SprintfFunc()
	cbYellow = color.New(color.Bold, color.FgHiYellow).SprintfFunc()
	cbRed    = color.New(color.Bold, color.FgHiRed).SprintfFunc()
)

// Section displays a section to the user
func Section(text string) {
	Note("")
	fmt.Println(cbBlue("["), cbYellow("**"), cbBlue("]"), "--", text, "--")
	Note("")
}

// Note displays a note to the user
func Note(text string) {
	fmt.Println(cbBlue("["), cbYellow("**"), cbBlue("]"), text)
}

// Query asks for a value from the user
func Query(prompt string) (string, error) {
	fmt.Print(cbBlue("[ "), cbYellow("??"), cbBlue(" ] "), prompt)

	in := bufio.NewReader(os.Stdin)
	response, err := in.ReadString('\n')
	return strings.TrimRight(response, "\r\n"), err
}

// QueryNoEcho asks for a value from the user without echoing what they type
func QueryNoEcho(prompt string) (string, error) {
	fmt.Print(cbBlue("[ "), cbYellow("??"), cbBlue(" ] "), prompt)

	response, err := terminal.ReadPassword(int(syscall.Stdin))
	fmt.Print("\n")
	return string(response), err
}

// QueryDefault asks for a value, falling back to a default
func QueryDefault(prompt string, defaultValue string) (string, error) {
	response, err := Query(prompt)

	if err != nil {
		return "", err
	}

	if len(strings.TrimSpace(response)) < 1 {
		return defaultValue, nil
	}
	return response, nil
}

// QueryBool asks for a true/false value from the user
func QueryBool(prompt string) (bool, error) {
	for {
		response, err := Query(prompt)
		if err != nil {
			return false, err
		}

		response = strings.ToLower(strings.TrimSpace(response))
		if len(response) < 1 {
			continue
		}

		// check for yes/true/1 or no/false/0
		if strings.Contains("yt1", string(response[0])) {
			return true, nil
		} else if strings.Contains("nf0", string(response[0])) {
			return false, nil
		}
	}
}

// Warn warns the user about something
func Warn(text string) {
	fmt.Println(cbBlue("["), cbRed("**"), cbBlue("]"), text)
}

// Error shows the user an error
func Error(text string) {
	fmt.Println(cbBlue("["), cbRed("!!"), cbBlue("]"), cbRed(text))
}

// InitialSetup performs the initial gircbnc setup
func InitialSetup(db *sql.DB) {
	fmt.Println(cbBlue("["), cbCyan("~~"), cbBlue("]"), "Welcome to", cbCyan("gIRCbnc"))
	Note("We will now run through basic setup.")

	var err error

	// generate the password salt used by the bouncer
	bncSalt, err := ircbnc.NewSalt()
	if err != nil {
		log.Fatal("Could not generate cryptographically-secure salt for the bouncer:", err.Error())
	}

	db.Exec(`INSERT INTO ircbnc (key, value) VALUES ("salt",?)`, base64.StdEncoding.EncodeToString(bncSalt))

	Section("Admin user settings")
	var username string
	var goodUsername string
	for {
		username, err = Query("Username: ")
		if err != nil {
			log.Fatal(err.Error())
		}

		username = strings.TrimSpace(username)

		goodUsername, err = ircbnc.BncName(username)
		if err == nil {
			Note(fmt.Sprintf("Username is %s. Will be stored internally as %s.", username, goodUsername))
			break
		} else {
			Error(err.Error())
		}
	}

	// generate our salts
	userSalt, err := ircbnc.NewSalt()
	if err != nil {
		log.Fatal("Could not generate cryptographically-secure salt for the user:", err.Error())
	}

	var passHash []byte
	for {
		password, err := QueryNoEcho("Enter password: ")

		if err != nil {
			Error(fmt.Sprintf("Error reading input line: %s", err.Error()))
			continue
		}

		passwordCompare, err := QueryNoEcho("Confirm password: ")

		if err != nil {
			Error(fmt.Sprintf("Error reading input line: %s", err.Error()))
			continue
		}

		if password != passwordCompare {
			Warn("The supplied passwords do not match")
			continue
		}

		passHash, err = ircbnc.GenerateFromPassword(bncSalt, userSalt, password)

		if err == nil {
			break
		} else {
			Error(fmt.Sprintf("Could not generate password: %s", err.Error()))
			continue
		}
	}

	// get IRC details
	var ircNick string
	for {
		ircNick, err = QueryDefault(fmt.Sprintf("Enter Nickname [%s]: ", goodUsername), goodUsername)
		if err != nil {
			log.Fatal(err.Error())
		}

		ircNick, err = ircbnc.IrcName(ircNick, false)
		if err == nil {
			break
		} else {
			Error(err.Error())
		}
	}

	var ircFbNick string
	defaultFallbackNick := fmt.Sprintf("%s_", ircNick)
	for {
		ircFbNick, err = QueryDefault(fmt.Sprintf("Enter Fallback Nickname [%s]: ", defaultFallbackNick), defaultFallbackNick)
		if err != nil {
			log.Fatal(err.Error())
		}

		ircFbNick, err = ircbnc.IrcName(ircFbNick, false)
		if err == nil {
			break
		} else {
			Error(err.Error())
		}
	}

	var ircUser string
	for {
		ircUser, err = QueryDefault(fmt.Sprintf("Enter Username [%s]: ", goodUsername), goodUsername)
		if err != nil {
			log.Fatal(err.Error())
		}

		ircUser, err = ircbnc.IrcName(ircUser, false)
		if err == nil {
			break
		} else {
			Error(err.Error())
		}
	}

	var ircReal string
	ircReal, err = Query("Enter Realname: ")
	if err != nil {
		log.Fatal(err.Error())
	}

	db.Exec(`INSERT INTO users (id, salt, password, default_nickname, default_fallback_nickname, default_username, default_realname) VALUES (?,?,?,?,?,?,?)`,
		goodUsername, userSalt, passHash, ircNick, ircFbNick, ircUser, ircReal)

	// now setup default networks for that user
	Section("Network Setup")

	for {
		setupNewNet, err := QueryBool("Set up a network? (y/n) ")
		if err != nil {
			log.Fatal(err.Error())
		}

		if !setupNewNet {
			break
		}

		var goodNetName string
		for {
			netName, err := Query("Name (e.g. freenode): ")
			if err != nil {
				log.Fatal(err.Error())
			}

			goodNetName, err = ircbnc.BncName(netName)
			if err == nil {
				Note(fmt.Sprintf("Network name is %s. Will be stored internally as %s.", netName, goodNetName))
				break
			} else {
				Error(err.Error())
			}
		}

		var serverAddress string
		for {
			serverAddress, err = Query("Server host (e.g. chat.freenode.net): ")
			if err != nil {
				log.Fatal(err.Error())
			}

			if len(strings.TrimSpace(serverAddress)) < 1 {
				Error("Hostname must have at least one character!")
				continue
			}

			break
		}

		serverUseTLS, err := QueryBool("Server uses SSL/TLS? (y/n) ")
		if err != nil {
			log.Fatal(err.Error())
		}

		var defaultPort int
		if serverUseTLS {
			defaultPort = 6697
		} else {
			defaultPort = 6667
		}

		var serverPort int
		for {
			portString, err := QueryDefault(fmt.Sprintf("Server Port [%s]: ", strconv.Itoa(defaultPort)), strconv.Itoa(defaultPort))
			if err != nil {
				log.Fatal(err.Error())
			}

			serverPort, err = strconv.Atoi(portString)
			if err != nil {
				Error(err.Error())
				continue
			}

			if (serverPort < 1) || (serverPort > 65535) {
				Error("Port number can be 1 - 65535")
				continue
			}

			break
		}

		serverPass, err := Query("Server connection password (probably empty): ")
		if err != nil {
			log.Fatal(err.Error())
		}

		var serverChannels []string
		for {
			serverChannelsString, err := Query("Channels to autojoin (separated by spaces): ")
			if err != nil {
				log.Fatal(err.Error())
			}

			serverChannels = make([]string, 0)

			for _, channel := range strings.Fields(serverChannelsString) {
				channel, err := ircbnc.IrcName(channel, true)
				if err != nil {
					break
				}

				serverChannels = append(serverChannels, channel)
			}

			if err != nil {
				Error(err.Error())
				continue
			}

			break
		}

		db.Exec(`INSERT INTO server_connections (user_id, name, password) VALUES (?,?,?)`,
			goodUsername, goodNetName, serverPass)
		db.Exec(`INSERT INTO server_connection_addresses (user_id, sc_name, address, port, use_tls) VALUES (?,?,?,?,?)`,
			goodUsername, goodNetName, serverAddress, serverPort, serverUseTLS)
		for _, channel := range serverChannels {
			db.Exec(`INSERT INTO server_connection_channels (user_id, sc_name, name) VALUES (?,?,?)`,
				goodUsername, goodNetName, channel)
		}
	}

	fmt.Println(cbBlue("["), cbCyan("~~"), cbBlue("]"), cbCyan("gIRCbnc"), "is now configured!")
	Note("You can now launch gIRCbnc and connect to it with your IRC client")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fsnotify/fsnotify"
)

// Entry point for application to start runonchange logic, once preferences and
// settings have been taken care of (parsing CLI flags, basic validation, OS signal
// mgmt) upstream by main.
//
// runonchange logic we need to setup:
// - worker to watch and filter Filesystem events
// - worker to handle filtered events and invoke COMMAND
// - configuration of filesystem event library
// - kick off an initial, sample COMMAND invocation
func (run *runDirective) setup() error {
	watcher, e := fsnotify.NewWatcher()
	if e != nil {
		return fmt.Errorf("starting FS watchers: %v", e)
	}
	run.fsWatcher = watcher

	fsEvents := make(chan fsnotify.Event)
	go run.watchFSEvents(fsEvents)
	go run.handleFSEvents(fsEvents)

	dirCount, e := run.registerDirectoriesToWatch()
	if e != nil {
		return fmt.Errorf("registering FS watchers: %v", e)
	}
	run.reportEstablishedWatches(dirCount)

	// Start an initial run before we even get FS events.
	go run.maybeRun(nil /*event*/, true /*msgStdout*/)

	return nil
}
<commit_msg>(maybe) bugfix: pointer left behind on stack?<commit_after>package main

import (
	"fmt"
	"github.com/fsnotify/fsnotify"
)

// Entry point for application to start runonchange logic, once preferences and
// settings have been taken care of (parsing CLI flags, basic validation, OS signal
// mgmt) upstream by main.
//
// runonchange logic we need to setup:
// - worker to watch and filter Filesystem events
// - worker to handle filtered events and invoke COMMAND
// - configuration of filesystem event library
// - kick off an initial, sample COMMAND invocation
func (run *runDirective) setup() error {
	watcher, e := fsnotify.NewWatcher()
	if e != nil {
		return fmt.Errorf("starting FS watchers: %v", e)
	}
	run.fsWatcher = watcher

	fsEvents := make(chan fsnotify.Event)
	go func() {
		run.watchFSEvents(fsEvents)
	}()
	go func() {
		run.handleFSEvents(fsEvents)
	}()

	dirCount, e := run.registerDirectoriesToWatch()
	if e != nil {
		return fmt.Errorf("registering FS watchers: %v", e)
	}
	run.reportEstablishedWatches(dirCount)

	// Start an initial run before we even get FS events.
	go run.maybeRun(nil /*event*/, true /*msgStdout*/)

	return nil
}
<|endoftext|>


<commit_before>package operationlock

import (
	"fmt"
	"sync"
	"time"

	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/shared/logger"
)

// TimeoutDefault timeout the operation lock will be kept alive for without needing to call Reset().
const TimeoutDefault time.Duration = time.Second * time.Duration(30)

// TimeoutShutdown timeout that can be used when shutting down an instance.
const TimeoutShutdown time.Duration = time.Minute * time.Duration(5)

// Action indicates the operation action type.
type Action string

// ActionCreate for creating an instance.
const ActionCreate Action = "create"

// ActionStart for starting an instance.
const ActionStart Action = "start"

// ActionStop for stopping an instance.
const ActionStop Action = "stop"

// ActionRestart for restarting an instance.
const ActionRestart Action = "restart"

// ActionRestore for restoring an instance.
const ActionRestore Action = "restore"

// ActionUpdate for updating an instance.
const ActionUpdate Action = "update"

// ErrNonReusuableSucceeded is returned when no operation is created due to having to wait for a matching
// non-reusuable operation that has now completed successfully.
var ErrNonReusuableSucceeded error = fmt.Errorf("A matching non-reusable operation has now succeeded")

var instanceOperationsLock sync.Mutex
var instanceOperations = make(map[string]*InstanceOperation)

// InstanceOperation operation locking.
type InstanceOperation struct {
	action       Action
	chanDone     chan error
	chanReset    chan time.Duration
	err          error
	projectName  string
	instanceName string
	reusable     bool
}

// Create creates a new operation lock for an Instance if one does not already exist and returns it.
// The lock will be released after TimeoutDefault or when Done() is called, which ever occurs first.
// If createReusuable is set as true then future lock attempts can specify the reuseExisting argument as true
// which will then trigger a reset of the timeout to TimeoutDefault on the existing lock and return it.
func Create(projectName string, instanceName string, action Action, createReusuable bool, reuseExisting bool) (*InstanceOperation, error) {
	if projectName == "" || instanceName == "" {
		return nil, fmt.Errorf("Invalid project or instance name")
	}

	instanceOperationsLock.Lock()
	defer instanceOperationsLock.Unlock()

	opKey := project.Instance(projectName, instanceName)

	op := instanceOperations[opKey]
	if op != nil {
		if op.reusable && reuseExisting {
			// Reset operation timeout without releasing lock or deadlocking using Reset() function.
			op.chanReset <- TimeoutDefault
			logger.Debug("Instance operation lock reused", logger.Ctx{"project": op.projectName, "instance": op.instanceName, "action": op.action, "reusable": op.reusable})

			return op, nil
		}

		return nil, fmt.Errorf("Instance is busy running a %q operation", op.action)
	}

	op = &InstanceOperation{}
	op.projectName = projectName
	op.instanceName = instanceName
	op.action = action
	op.reusable = createReusuable
	op.chanDone = make(chan error)
	op.chanReset = make(chan time.Duration)

	instanceOperations[opKey] = op
	logger.Debug("Instance operation lock created", logger.Ctx{"project": op.projectName, "instance": op.instanceName, "action": op.action, "reusable": op.reusable})

	go func(op *InstanceOperation) {
		timeout := TimeoutDefault
		timer := time.NewTimer(timeout)
		defer timer.Stop()

		for {
			select {
			case <-op.chanDone:
				return
			case timeout = <-op.chanReset:
				if !timer.Stop() {
					<-timer.C
				}

				timer.Reset(timeout)
				continue
			case <-timer.C:
				op.Done(fmt.Errorf("Instance %q operation timed out after %v", op.action, timeout))
				return
			}
		}
	}(op)

	return op, nil
}

// CreateWaitGet is a weird function which does what we happen to want most of the time.
//
// If the instance has an operation of the same type and it's not reusable
// or the caller doesn't want to reuse it, the function will wait and
// indicate that it did so.
//
// If the instance has an existing operation of one of the inheritableActions types, then the operation is returned
// to the user. This allows an operation started in one function/routine to be inherited by another.
//
// If the instance doesn't have an ongoing operation, has an operation of a different type that is not in the
// inheritableActions list or has the right type and is being reused, then this behaves as a Create call.
//
// Returns ErrWaitedForMatching if it waited for a matching operation to finish and it's finished successfully and
// so didn't return create a new operation.
func CreateWaitGet(projectName string, instanceName string, action Action, inheritableActions []Action, createReusuable bool, reuseExisting bool) (*InstanceOperation, error) {
	op := Get(projectName, instanceName)

	// No existing operation, call create.
	if op == nil {
		op, err := Create(projectName, instanceName, action, createReusuable, reuseExisting)
		return op, err
	}

	// Operation action matches but is not reusable or we have been asked not to reuse,
	// so wait and return result.
	if op.action == action && (!reuseExisting || !op.reusable) {
		err := op.Wait()
		if err != nil {
			return nil, err
		}

		// The matching operation ended without error, but this means we've not created a new
		// operation for this request, so return a special error indicating this scenario.
		return nil, ErrNonReusuableSucceeded
	}

	// Operation action matches one the inheritable actions, return the operation.
	if op.ActionMatch(inheritableActions...) {
		logger.Debug("Instance operation lock inherited", logger.Ctx{"project": op.projectName, "instance": op.instanceName, "action": op.action, "reusable": op.reusable, "inheritedByAction": action})

		return op, nil
	}

	// Send the rest to Create to try and create a new operation.
	op, err := Create(projectName, instanceName, action, createReusuable, reuseExisting)

	return op, err
}

// Get retrieves an existing lock or returns nil if no lock exists.
func Get(projectName string, instanceName string) *InstanceOperation {
	instanceOperationsLock.Lock()
	defer instanceOperationsLock.Unlock()

	opKey := project.Instance(projectName, instanceName)

	return instanceOperations[opKey]
}

// Action returns operation's action.
func (op *InstanceOperation) Action() Action {
	// This function can be called on a nil struct.
	if op == nil {
		return ""
	}

	return op.action
}

// ActionMatch returns true if operations' action matches on of the matchActions.
func (op *InstanceOperation) ActionMatch(matchActions ...Action) bool {
	for _, matchAction := range matchActions {
		if op.action == matchAction {
			return true
		}
	}

	return false
}

// Reset resets the operation using TimeoutDefault until it expires.
func (op *InstanceOperation) Reset() error {
	return op.ResetTimeout(TimeoutDefault)
}

// ResetTimeout resets the operation using a custom timeout until it expires.
func (op *InstanceOperation) ResetTimeout(timeout time.Duration) error {
	// This function can be called on a nil struct.
	if op == nil {
		return nil
	}

	instanceOperationsLock.Lock()
	defer instanceOperationsLock.Unlock()

	opKey := project.Instance(op.projectName, op.instanceName)

	// Check if already done
	runningOp, ok := instanceOperations[opKey]
	if !ok || runningOp != op {
		return fmt.Errorf("Operation is already done or expired")
	}

	op.chanReset <- timeout
	return nil
}

// Wait waits for an operation to finish.
func (op *InstanceOperation) Wait() error {
	// This function can be called on a nil struct.
	if op == nil {
		return nil
	}

	<-op.chanDone

	return op.err
}

// Done indicates the operation has finished.
func (op *InstanceOperation) Done(err error) {
	// This function can be called on a nil struct.
	if op == nil {
		return
	}

	instanceOperationsLock.Lock()
	defer instanceOperationsLock.Unlock()

	opKey := project.Instance(op.projectName, op.instanceName)

	// Check if already done.
	runningOp, ok := instanceOperations[opKey]
	if !ok || runningOp != op {
		return
	}

	op.err = err
	delete(instanceOperations, opKey) // Delete before closing chanDone.
	close(op.chanDone)
	logger.Debug("Instance operation lock finished", logger.Ctx{"project": op.projectName, "instance": op.instanceName, "action": op.action, "reusable": op.reusable, "err": err})
}
<commit_msg>lxd/instance/operationlock: Adds ActionDelete constant<commit_after>package operationlock

import (
	"fmt"
	"sync"
	"time"

	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/shared/logger"
)

// TimeoutDefault timeout the operation lock will be kept alive for without needing to call Reset().
const TimeoutDefault time.Duration = time.Second * time.Duration(30)

// TimeoutShutdown timeout that can be used when shutting down an instance.
const TimeoutShutdown time.Duration = time.Minute * time.Duration(5)

// Action indicates the operation action type.
type Action string

// ActionCreate for creating an instance.
const ActionCreate Action = "create"

// ActionStart for starting an instance.
const ActionStart Action = "start"

// ActionStop for stopping an instance.
const ActionStop Action = "stop"

// ActionRestart for restarting an instance.
const ActionRestart Action = "restart"

// ActionRestore for restoring an instance.
const ActionRestore Action = "restore"

// ActionUpdate for updating an instance.
const ActionUpdate Action = "update"

// ActionDelete for deleting an instance.
const ActionDelete Action = "delete"

// ErrNonReusuableSucceeded is returned when no operation is created due to having to wait for a matching
// non-reusuable operation that has now completed successfully.
var ErrNonReusuableSucceeded error = fmt.Errorf("A matching non-reusable operation has now succeeded")

var instanceOperationsLock sync.Mutex
var instanceOperations = make(map[string]*InstanceOperation)

// InstanceOperation operation locking.
type InstanceOperation struct {
	action       Action
	chanDone     chan error
	chanReset    chan time.Duration
	err          error
	projectName  string
	instanceName string
	reusable     bool
}

// Create creates a new operation lock for an Instance if one does not already exist and returns it.
// The lock will be released after TimeoutDefault or when Done() is called, which ever occurs first.
// If createReusuable is set as true then future lock attempts can specify the reuseExisting argument as true
// which will then trigger a reset of the timeout to TimeoutDefault on the existing lock and return it.
func Create(projectName string, instanceName string, action Action, createReusuable bool, reuseExisting bool) (*InstanceOperation, error) {
	if projectName == "" || instanceName == "" {
		return nil, fmt.Errorf("Invalid project or instance name")
	}

	instanceOperationsLock.Lock()
	defer instanceOperationsLock.Unlock()

	opKey := project.Instance(projectName, instanceName)

	op := instanceOperations[opKey]
	if op != nil {
		if op.reusable && reuseExisting {
			// Reset operation timeout without releasing lock or deadlocking using Reset() function.
			op.chanReset <- TimeoutDefault
			logger.Debug("Instance operation lock reused", logger.Ctx{"project": op.projectName, "instance": op.instanceName, "action": op.action, "reusable": op.reusable})

			return op, nil
		}

		return nil, fmt.Errorf("Instance is busy running a %q operation", op.action)
	}

	op = &InstanceOperation{}
	op.projectName = projectName
	op.instanceName = instanceName
	op.action = action
	op.reusable = createReusuable
	op.chanDone = make(chan error)
	op.chanReset = make(chan time.Duration)

	instanceOperations[opKey] = op
	logger.Debug("Instance operation lock created", logger.Ctx{"project": op.projectName, "instance": op.instanceName, "action": op.action, "reusable": op.reusable})

	go func(op *InstanceOperation) {
		timeout := TimeoutDefault
		timer := time.NewTimer(timeout)
		defer timer.Stop()

		for {
			select {
			case <-op.chanDone:
				return
			case timeout = <-op.chanReset:
				if !timer.Stop() {
					<-timer.C
				}

				timer.Reset(timeout)
				continue
			case <-timer.C:
				op.Done(fmt.Errorf("Instance %q operation timed out after %v", op.action, timeout))
				return
			}
		}
	}(op)

	return op, nil
}

// CreateWaitGet is a weird function which does what we happen to want most of the time.
//
// If the instance has an operation of the same type and it's not reusable
// or the caller doesn't want to reuse it, the function will wait and
// indicate that it did so.
//
// If the instance has an existing operation of one of the inheritableActions types, then the operation is returned
// to the user. This allows an operation started in one function/routine to be inherited by another.
//
// If the instance doesn't have an ongoing operation, has an operation of a different type that is not in the
// inheritableActions list or has the right type and is being reused, then this behaves as a Create call.
//
// Returns ErrWaitedForMatching if it waited for a matching operation to finish and it's finished successfully and
// so didn't return create a new operation.
func CreateWaitGet(projectName string, instanceName string, action Action, inheritableActions []Action, createReusuable bool, reuseExisting bool) (*InstanceOperation, error) {
	op := Get(projectName, instanceName)

	// No existing operation, call create.
	if op == nil {
		op, err := Create(projectName, instanceName, action, createReusuable, reuseExisting)
		return op, err
	}

	// Operation action matches but is not reusable or we have been asked not to reuse,
	// so wait and return result.
	if op.action == action && (!reuseExisting || !op.reusable) {
		err := op.Wait()
		if err != nil {
			return nil, err
		}

		// The matching operation ended without error, but this means we've not created a new
		// operation for this request, so return a special error indicating this scenario.
		return nil, ErrNonReusuableSucceeded
	}

	// Operation action matches one the inheritable actions, return the operation.
	if op.ActionMatch(inheritableActions...) {
		logger.Debug("Instance operation lock inherited", logger.Ctx{"project": op.projectName, "instance": op.instanceName, "action": op.action, "reusable": op.reusable, "inheritedByAction": action})

		return op, nil
	}

	// Send the rest to Create to try and create a new operation.
	op, err := Create(projectName, instanceName, action, createReusuable, reuseExisting)

	return op, err
}

// Get retrieves an existing lock or returns nil if no lock exists.
func Get(projectName string, instanceName string) *InstanceOperation {
	instanceOperationsLock.Lock()
	defer instanceOperationsLock.Unlock()

	opKey := project.Instance(projectName, instanceName)

	return instanceOperations[opKey]
}

// Action returns operation's action.
func (op *InstanceOperation) Action() Action {
	// This function can be called on a nil struct.
	if op == nil {
		return ""
	}

	return op.action
}

// ActionMatch returns true if operations' action matches on of the matchActions.
func (op *InstanceOperation) ActionMatch(matchActions ...Action) bool {
	for _, matchAction := range matchActions {
		if op.action == matchAction {
			return true
		}
	}

	return false
}

// Reset resets the operation using TimeoutDefault until it expires.
func (op *InstanceOperation) Reset() error {
	return op.ResetTimeout(TimeoutDefault)
}

// ResetTimeout resets the operation using a custom timeout until it expires.
func (op *InstanceOperation) ResetTimeout(timeout time.Duration) error {
	// This function can be called on a nil struct.
	if op == nil {
		return nil
	}

	instanceOperationsLock.Lock()
	defer instanceOperationsLock.Unlock()

	opKey := project.Instance(op.projectName, op.instanceName)

	// Check if already done
	runningOp, ok := instanceOperations[opKey]
	if !ok || runningOp != op {
		return fmt.Errorf("Operation is already done or expired")
	}

	op.chanReset <- timeout
	return nil
}

// Wait waits for an operation to finish.
func (op *InstanceOperation) Wait() error {
	// This function can be called on a nil struct.
	if op == nil {
		return nil
	}

	<-op.chanDone

	return op.err
}

// Done indicates the operation has finished.
func (op *InstanceOperation) Done(err error) {
	// This function can be called on a nil struct.
	if op == nil {
		return
	}

	instanceOperationsLock.Lock()
	defer instanceOperationsLock.Unlock()

	opKey := project.Instance(op.projectName, op.instanceName)

	// Check if already done.
	runningOp, ok := instanceOperations[opKey]
	if !ok || runningOp != op {
		return
	}

	op.err = err
	delete(instanceOperations, opKey) // Delete before closing chanDone.
	close(op.chanDone)
	logger.Debug("Instance operation lock finished", logger.Ctx{"project": op.projectName, "instance": op.instanceName, "action": op.action, "reusable": op.reusable, "err": err})
}
<|endoftext|>


<commit_before>package middleware

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestInclude(t *testing.T) {
	context := initTempContext()

	inputFilename := "test_file"
	absInFilePath := filepath.Join(fmt.Sprintf("%s", context.Root), inputFilename)
	defer func() {
		err := os.Remove(absInFilePath)
		if err != nil && !os.IsNotExist(err) {
			t.Fatalf("Failed to clean test file!")
		}
	}()

	tests := []struct {
		fileContent          string
		expectedContent      string
		shouldErr            bool
		expectedErrorContent string
	}{
		// Test 0 - all good
		{
			fileContent:          `str1 {{ .Root }} str2`,
			expectedContent:      fmt.Sprintf("str1 %s str2", context.Root),
			shouldErr:            false,
			expectedErrorContent: "",
		},
		// Test 1 - failure on template.Parse
		{
			fileContent:          `str1 {{ .Root } str2`,
			expectedContent:      "",
			shouldErr:            true,
			expectedErrorContent: `unexpected "}" in operand`,
		},
		// Test 3 - failure on template.Execute
		{
			fileContent:          `str1 {{ .InvalidField }} str2`,
			expectedContent:      "",
			shouldErr:            true,
			expectedErrorContent: `InvalidField is not a field of struct type middleware.Context`,
		},
	}

	for i, test := range tests {
		testPrefix := fmt.Sprintf("Test [%d]: ", i)

		// WriteFile truncates the contentt
		err := ioutil.WriteFile(absInFilePath, []byte(test.fileContent), os.ModePerm)
		if err != nil {
			t.Fatal(testPrefix+"Failed to create test file. Error was: %v", err)
		}

		content, err := context.Include(inputFilename)
		if err != nil {
			if !test.shouldErr {
				t.Errorf(testPrefix+"Expected no error, found [%s]", test.expectedErrorContent, err.Error())
			}
			if !strings.Contains(err.Error(), test.expectedErrorContent) {
				t.Errorf(testPrefix+"Expected error content [%s], found [%s]", test.expectedErrorContent, err.Error())
			}
		}

		if err == nil && test.shouldErr {
			t.Errorf(testPrefix+"Expected error [%s] but found nil. Input file was: %s", test.expectedErrorContent, inputFilename)
		}

		if content != test.expectedContent {
			t.Errorf(testPrefix+"Expected content [%s] but found [%s]. Input file was: %s", test.expectedContent, content, inputFilename)
		}
	}
}

func TestIncludeNotExisting(t *testing.T) {
	context := initTempContext()

	_, err := context.Include("not_existing")
	if err == nil {
		t.Errorf("Expected error but found nil!")
	}
}

func initTempContext() Context {
	rootDir := getTestFilesFolder()
	return Context{Root: http.Dir(rootDir)}
}

func getTestFilesFolder() string {
	return os.TempDir()
}
<commit_msg>add tests for context.Cookie() and context.IP()<commit_after>package middleware

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

func TestInclude(t *testing.T) {
	context, err := initTestContext()
	if err != nil {
		t.Fatalf("Failed to prepare test context")
	}

	inputFilename := "test_file"
	absInFilePath := filepath.Join(fmt.Sprintf("%s", context.Root), inputFilename)
	defer func() {
		err := os.Remove(absInFilePath)
		if err != nil && !os.IsNotExist(err) {
			t.Fatalf("Failed to clean test file!")
		}
	}()

	tests := []struct {
		fileContent          string
		expectedContent      string
		shouldErr            bool
		expectedErrorContent string
	}{
		// Test 0 - all good
		{
			fileContent:          `str1 {{ .Root }} str2`,
			expectedContent:      fmt.Sprintf("str1 %s str2", context.Root),
			shouldErr:            false,
			expectedErrorContent: "",
		},
		// Test 1 - failure on template.Parse
		{
			fileContent:          `str1 {{ .Root } str2`,
			expectedContent:      "",
			shouldErr:            true,
			expectedErrorContent: `unexpected "}" in operand`,
		},
		// Test 3 - failure on template.Execute
		{
			fileContent:          `str1 {{ .InvalidField }} str2`,
			expectedContent:      "",
			shouldErr:            true,
			expectedErrorContent: `InvalidField is not a field of struct type middleware.Context`,
		},
	}

	for i, test := range tests {
		testPrefix := getTestPrefix(i)

		// WriteFile truncates the contentt
		err := ioutil.WriteFile(absInFilePath, []byte(test.fileContent), os.ModePerm)
		if err != nil {
			t.Fatal(testPrefix+"Failed to create test file. Error was: %v", err)
		}

		content, err := context.Include(inputFilename)
		if err != nil {
			if !test.shouldErr {
				t.Errorf(testPrefix+"Expected no error, found [%s]", test.expectedErrorContent, err.Error())
			}
			if !strings.Contains(err.Error(), test.expectedErrorContent) {
				t.Errorf(testPrefix+"Expected error content [%s], found [%s]", test.expectedErrorContent, err.Error())
			}
		}

		if err == nil && test.shouldErr {
			t.Errorf(testPrefix+"Expected error [%s] but found nil. Input file was: %s", test.expectedErrorContent, inputFilename)
		}

		if content != test.expectedContent {
			t.Errorf(testPrefix+"Expected content [%s] but found [%s]. Input file was: %s", test.expectedContent, content, inputFilename)
		}
	}
}

func TestIncludeNotExisting(t *testing.T) {
	context, err := initTestContext()
	if err != nil {
		t.Fatalf("Failed to prepare test context")
	}

	_, err = context.Include("not_existing")
	if err == nil {
		t.Errorf("Expected error but found nil!")
	}
}

func TestCookie(t *testing.T) {

	tests := []struct {
		cookie        *http.Cookie
		cookieName    string
		expectedValue string
	}{
		// Test 0 - happy path
		{
			cookie:        &http.Cookie{Name: "cookieName", Value: "cookieValue"},
			cookieName:    "cookieName",
			expectedValue: "cookieValue",
		},
		// Test 1 - try to get a non-existing cookie
		{
			cookie:        &http.Cookie{Name: "cookieName", Value: "cookieValue"},
			cookieName:    "notExisting",
			expectedValue: "",
		},
		// Test 2 - partial name match
		{
			cookie:        &http.Cookie{Name: "cookie", Value: "cookieValue"},
			cookieName:    "cook",
			expectedValue: "",
		},
		// Test 3 - cookie with optional fields
		{
			cookie:        &http.Cookie{Name: "cookie", Value: "cookieValue", Path: "/path", Domain: "https://caddy.com", Expires: (time.Now().Add(10 * time.Minute)), MaxAge: 120},
			cookieName:    "cookie",
			expectedValue: "cookieValue",
		},
	}

	for i, test := range tests {
		testPrefix := getTestPrefix(i)

		// reinitialize the context for each test
		context, err := initTestContext()
		if err != nil {
			t.Fatalf("Failed to prepare test context")
		}
		context.Req.AddCookie(test.cookie)

		actualCookieVal := context.Cookie(test.cookieName)

		if actualCookieVal != test.expectedValue {
			t.Errorf(testPrefix+"Expected cookie value [%s] but found [%s] for cookie with name %s", test.expectedValue, actualCookieVal, test.cookieName)
		}
	}
}

func TestCookieMultipleCookies(t *testing.T) {
	context, err := initTestContext()
	if err != nil {
		t.Fatalf("Failed to prepare test context")
	}

	cookieNameBase, cookieValueBase := "cookieName", "cookieValue"

	// make sure that there's no state and multiple requests for different cookies return the correct result
	for i := 0; i < 10; i++ {
		context.Req.AddCookie(&http.Cookie{Name: fmt.Sprintf("%s%d", cookieNameBase, i), Value: fmt.Sprintf("%s%d", cookieValueBase, i)})
	}

	for i := 0; i < 10; i++ {
		expectedCookieVal := fmt.Sprintf("%s%d", cookieValueBase, i)
		actualCookieVal := context.Cookie(fmt.Sprintf("%s%d", cookieNameBase, i))
		if actualCookieVal != expectedCookieVal {
			t.Fatalf("Expected cookie value %s, found %s", expectedCookieVal, actualCookieVal)
		}
	}
}

func TestIP(t *testing.T) {
	context, err := initTestContext()
	if err != nil {
		t.Fatalf("Failed to prepare test context")
	}

	tests := []struct {
		inputRemoteAddr string
		expectedIP      string
	}{
		// Test 0 - ipv4 with port
		{"1.1.1.1:1111", "1.1.1.1"},
		// Test 1 - ipv4 without port
		{"1.1.1.1", "1.1.1.1"},
		// Test 2 - ipv6 with port
		{"[::1]:11", "::1"},
		// Test 3 - ipv6 without port and brackets
		{"[2001:db8:a0b:12f0::1]", "[2001:db8:a0b:12f0::1]"},
		// Test 4 - ipv6 with zone and port
		{`[fe80:1::3%eth0]:44`, `fe80:1::3%eth0`},
		// Test 5 - ipv6 without port with brackets
		// {"[:fe:2]", ":fe:2"}, // TODO - failing (error in SplitHostPort) returns the host with brackets
		// Test 6 - invalid address
		// {":::::::::::::", ""}, // TODO - failing (error in SplitHostPort) returns the invalid address
		// Test 7 - invalid address
		// {"[::1][]", ""}, // TODO - failing (error in SplitHostPort) returns the invalid address
	}

	for i, test := range tests {
		testPrefix := getTestPrefix(i)

		context.Req.RemoteAddr = test.inputRemoteAddr
		actualIP := context.IP()

		if actualIP != test.expectedIP {
			t.Errorf(testPrefix+"Expected IP %s, found %s", test.expectedIP, actualIP)
		}
	}
}

func initTestContext() (Context, error) {
	rootDir := getTestFilesFolder()
	body := bytes.NewBufferString("request body")
	request, err := http.NewRequest("GET", "https://caddy.com", body)
	if err != nil {
		return Context{}, err
	}
	return Context{Root: http.Dir(rootDir), Req: request}, nil
}

func getTestFilesFolder() string {
	return os.TempDir()
}

func getTestPrefix(testN int) string {
	return fmt.Sprintf("Test [%d]: ", testN)
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package migration

import (
	"github.com/juju/errors"
	"github.com/juju/version"

	"github.com/juju/juju/state"
)

// PrecheckShim wraps a *state.State to implement PrecheckBackend.
func PrecheckShim(st *state.State) PrecheckBackend {
	return &precheckShim{st}
}

// precheckShim is untested, but is small and simple enough to be
// verified by inspection.
type precheckShim struct {
	st *state.State
}

// NeedsCleanup implements PrecheckBackend.
func (s *precheckShim) NeedsCleanup() (bool, error) {
	return s.st.NeedsCleanup()
}

// AgentVersion implements PrecheckBackend.
func (s *precheckShim) AgentVersion() (version.Number, error) {
	cfg, err := s.st.ModelConfig()
	if err != nil {
		return version.Zero, errors.Trace(err)
	}
	vers, ok := cfg.AgentVersion()
	if !ok {
		return version.Zero, errors.New("no model agent version")
	}
	return vers, nil
}

// AllMachines implements PrecheckBackend.
func (s *precheckShim) AllMachines() ([]PrecheckMachine, error) {
	machines, err := s.st.AllMachines()
	if err != nil {
		return nil, errors.Trace(err)
	}
	out := make([]PrecheckMachine, 0, len(machines))
	for _, machine := range machines {
		out = append(out, machine)
	}
	return out, nil
}
<commit_msg>migration: Simplify precheckShim<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package migration

import (
	"github.com/juju/errors"
	"github.com/juju/version"

	"github.com/juju/juju/state"
)

// PrecheckShim wraps a *state.State to implement PrecheckBackend.
func PrecheckShim(st *state.State) PrecheckBackend {
	return &precheckShim{st}
}

// precheckShim is untested, but is simple enough to be verified by
// inspection.
type precheckShim struct {
	*state.State
}

// AgentVersion implements PrecheckBackend.
func (s *precheckShim) AgentVersion() (version.Number, error) {
	cfg, err := s.ModelConfig()
	if err != nil {
		return version.Zero, errors.Trace(err)
	}
	vers, ok := cfg.AgentVersion()
	if !ok {
		return version.Zero, errors.New("no model agent version")
	}
	return vers, nil
}

// AllMachines implements PrecheckBackend.
func (s *precheckShim) AllMachines() ([]PrecheckMachine, error) {
	machines, err := s.State.AllMachines()
	if err != nil {
		return nil, errors.Trace(err)
	}
	out := make([]PrecheckMachine, 0, len(machines))
	for _, machine := range machines {
		out = append(out, machine)
	}
	return out, nil
}
<|endoftext|>