For example I'm developing guestbook. It allows to add records, that need to be approved by moderator. But when developing further functionality, it's a pain to approve each record, that is added during testing.
So, is it possible to build dev version of application that creates such records with appropriate flag set?
For example prod build is compiled with the following fucntion:
func NewRecord() Record {
return Record{Moderation: Awaiting}
}
And in dev build is compiled with:
func NewRecord() Record {
return Record{Moderation: Approved}
}
I know in frontend, when you building some JS app, it's a common practice to set NODE_ENV=production environment variable when building for production.
I'm looking for something similar in Go.
I see two ways, but don't like any of them:
I can just set Awaiting = Approved while developing and then change it back to actual value when building prod version. But I afraid that one day I will forget about this mock, will commit it to repo or something like that.
Change function to something like
func NewRecord() Record {
if os.Getenv(mykey) == "production" {
return Record{Moderation: Awaiting}
} else {
return Record{Moderation: Approved}
}
}
But I don't like that this condition is evaluated in runtime for each new record. It just seems to be wrong for compiled language.
As a bonus it would be nice, if such application can show warning (to stdout/stderr) if it's build as dev version.
Thanks.
Personally I think using environment variables as you've done in your example is the "correct" way to do this. This allows you to tweak the behavior without rebuilding the application, which may be very useful while debugging. However, if you really want this done at compile time it can be accomplished with build constraints.
A built constraint is a special comment placed at the top of a file that says "only build this file if a specific condition is met". Conditions can be things like the machine architecture we're building on, the OS we're running, or custom build tags that the user specifies when building.
For example, you could tweak your function to something like:
func NewRecord() Record {
return Record{Moderation: ModLevel}
}
and then define ModLevel twice, once in a file modlevel_prod.go that looks like this (Note that the syntax for build constraints is changing, the second line is the new syntax and the first one wont' be valid for much longer. See the link at the end of this post for more info):
// +build !dev
//go:build !dev
package mypackage
const ModLevel = Awaiting
and one in modlevel_dev.go (or whever, the filename doesn't matter in this case) that looks like this:
// +build dev
//go:build dev
package mypackage
const ModLevel = Approved
Now, when you build, the production version of the file is the default and if you want to build the dev version of the file you have to explicitly include the build tag:
$ go build -tags dev
EDIT: the syntax for build constraints is changing. I have updated the examples to include both the old and new versions (which currently coexist). See https://golang.org/issues/41184 for more info and a timeline for deprecating the old one.
Evaluate the expression once at package initialization:
var defaultModeration int
func init() {
if os.Getenv(mykey) == "production" {
defaultModeration = Awaiting
} else {
defaultModeration = Approved
}
}
func NewRecord() Record { return Record{Moderation: defaultModeration} }
Related
I'm setting up some integration testing, which I'm doing in a separate test package to my src code. This is done to prevent circular dependencies. Unit tests are not stored here, they are stored alongside the files they are testing.
My golang project hierarchy looks like:
cmd
public
...
testing/
main_test.go
database_test.go
in main_test.go, I plan to initialise the connections to external dependencies, such as my test database.
package tests
type Database struct {
...
}
var DB Database
func TestMain(m *testing.M){
SetUpDatabase()
exitCode := m.Run()
os.Exit(exitCode)
}
database_integration_test.go
func Test(t *testing.T) {
tests := []struct {
title string
run func(t *testing.T)
}{
{"should make query", testQuery},
}
for _, test := range tests {
t.Run(test.title, func(t *testing.T) {
test.run(t)
})
}
}
func testQuery(t *testing.T) {
var r result.Result
err := DB.database.DoQuery("").Decode(&r)
if err != nil {
t.Errorf(err.Error())
t.Fatal()
}
}
This setup works when I run it, however, I would like to add build tags to these files, or the type: // +build integration
However, as soon as I use the build tag, the database_integration_test.go file cannot see the initalised Database type. How can I stop this? Also as a side note, should I change the name of main_test.go. I only called it that due to main being the standrd entry point.
Firstly, Regarding this:
Also as a side note, should I change the name of main_test.go. I only
called it that due to main being the standard entry point.
I think it is confusing to name it as main_test.go as it might indicate that you are testing the main function in this file (according to golang convention)
Secondly, Regarding this:
However, as soon as I use the build tag, the
database_integration_test.go file cannot see the initialised Database
type. How can I stop this?
A Build Constraint or also known as Build Tag is used to include or exclude files in a package during a build process. With this, we can build different types of builds from the same source code.
So if you are not seeing the Database Type initialized then most probably the definition of the Database Type and the integration test are tagged with different build tags. Make sure they are present in the same build tags. Also, I think you can use more than one build tag to label a file. So you can try that as well.
For more details on the Build Tags check out the following article by Dave Cheney here
You could simply add a flag to your TestMain:
var isIntegration bool
func init() {
flag.StringVar(&isIntegration, "mytest.integration", "Set flag to set up DB for integration tests")
}
func TestMain(t *testing.M) {
SetUpDatabase(isIntegration)
//etc...
}
Then just simply have the SetUpDatabase call different, unexported, functions based on whether or not the argument is true or false. That'd be a quick way to get the behaviour, without having to much about with custom build constraints. Especially considering you're running tests, not building the application as-such.
As far as renaming main_test.go is concerned: I don't see why you'd need to change it. It does what it says on the tin. When someone else wants to see how the tests are structured/run, or what possible flags have been added, it's a lot easier to just check the directory and look for a main_test.go file (along with init.go, that'd be first file I'd look for). Any other name like setup_integration_test.go, integration_setup_test.go, integration_start_test.go, ... is just going to muddy the waters.
Are there any conditions to the default environment variables being set on google cloud function?
I have the following code:
func init() {
projectID := os.Getenv("GCP_PROJECT")
log.Printf("projectID: %s\n", projectID)
functionName := os.Getenv("FUNCTION_NAME")
log.Printf("functoinName: %s\n", functionName)
region := os.Getenv("FUNCTION_REGION")
log.Printf("region: %s\n", region)
}
and the values are empty.
Even if I do:
func GameUpdate(ctx context.Context, e FirestoreEvent) error {
functionName := os.Getenv("FUNCTION_NAME")
log.Printf("functoinName: %s\n", functionName)
}
They are still empty.
According to documentation, I would expect them to be set and available. But they are not :|
EDIT:
I am using go 1.13 as runtime and as Armatorix mentioned, these env variables are not available in that runtime...
Why I needed them was to write a wrapper for cloud.google.com/go/logging to be able to tag the severity of the logs.
I ended up prepending my stdout logs with [INFO]/[ERROR], and creating a tag from it \[([A-Z]+)\].*. Bonus is that I don't have to do a network call in my function to ship the logs.
Still disappointing that these environment variables are not available.
So I've read the same documentation.
Here you've got the info that it works like this with go1.11 (And it works, I tested it out).
BUT for go1.13 these are not set. You can still do it manually.
Also I've checked which envs are set on 1.13 version.
From os.Envrion()
PATH=/layers/google.go.build/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
FUNCTION_SIGNATURE_TYPE=http
DEBIAN_FRONTEND=noninteractive
HOME=/root
K_REVISION=9
FUNCTION_TARGET=HelloWorld
PORT=8080
GOROOT=/usr/local/go/
CGO_ENABLED=1
PWD=/srv
K_SERVICE=function-1
So the env that you probably want to use is K_SERVICE
I have created a Feature Request on your behalf, in order for Cloud Functions Engineering team to implement the automatic set of these Environment Variables to the newer Runtime Versions, such as Node.js 10 and Go1.13.
You may "star" the issue so that it gets visibility and also include yourself in the "CC" section, in order to receive further updates posted on this thread.
I hope this helps.
I created a library for that very purpose:
github.com/ncruces/go-gcf/logging
But you're right, on the Go 1.13 runtime, those environment variables are missing. On the migration guide they suggest setting them when you deploy.
Later I found that the recommended way of doing this is to use structured logging.
// Structured logging can be used to set severity levels.
// See https://cloud.google.com/logging/docs/structured-logging.
fmt.Println(`{"message": "This has ERROR severity", "severity": "error"}`)
So now, I'm in the process of "deprecating" my library, and creating a new one, with a simpler approach:
github.com/ncruces/go-gcp/glog
This is simple enough that a library isn't really required, but it helps to correctly JSON escape message.
Is there any way I can check if the Go program is compiled with -race enabled at runtime (for e.g. logging/informational purposes)?
I checked the documentation, as well as the obvious locations (runtime/*), but I can't find anything.
Since Go 1.18 there's debug.ReadBuildInfo(), which provides this; for example:
func race() {
b, ok := debug.ReadBuildInfo()
if !ok {
fmt.Println("could not read build info")
return
}
for _, s := range b.Settings {
if s.Key == "-race" {
fmt.Println("-race=" + s.Value)
return
}
}
fmt.Println("-race=false")
}
Which will print -race=true or -race=false.
In most cases, simply using the debug.BuildInfo.String() method is probably the easiest, as it prints out other useful stuff as well.
Since this is available since Go 1.18, which is comparatively new at the time of writing, you may want to consider putting a // +build go.18 build tag and provide a shim for older Go versions.
Older pre-Go 1.18 answer, kept for posterity.
As far as I can find there is no simple check for this, but when -race is enabled the race build tag is set, so you can take advantage of that.
I made a new directory israce, and put two files there:
israce/race.go:
// +build race
// Package israce reports if the Go race detector is enabled.
package israce
// Enabled reports if the race detector is enabled.
const Enabled = true
israce/norace.go:
// +build !race
// Package israce reports if the Go race detector is enabled.
package israce
// Enabled reports if the race detector is enabled.
const Enabled = false
Due to the build tag only one of the two files will get compiled.
This is also how the Go standard library does it (race.go, norace.go), but since that's an internal package it can't be imported outside of the Go source base.
I am using TeamCity 9.0.2, and I would like to make a template implement another template, or make a build configuration implement more than one template.
Can this be achieved?
This was not available when you asked the question but since Team City 10 you can now use Kotlin to configure your builds and thus your templates.
From this you can make Templates implement other Templates.
I myself have made Templates inherit from other templates to cut down on reconfiguration time and to not have to repeat myself so many times.
open class TheBaseTemplate(uuidIn: String, extIdIn: String, nameIn: String, additionalSettings: Template.() -> Unit) : Template({
uuid = uuidIn
extId = extIdIn
name = nameIn
/* all the other settings that are the same for the derived templates*/
additionalSettings()
})
object DerivedTemplateA : TheBaseTemplate("myUuidA", "myExtIdA", "myNameA", {
params {
param("set this", "to this")
}
})
object DerivedTemplateB : TheBaseTemplate("myUuidB", "myExtIdB", "myNameB", {
params {
param("set this", "to that")
}
})
object Project : Project({
uuid = "project uuid"
extId = "project extid"
name = "project name"
buildType {
template(DerivedTemplateA)
/* the uuid, extId and name are set here */
}
buildType {
template(DerivedTemplateB)
/* the uuid, extId and name are set here */
}
template(DerivedTemplateA)
template(DerivedTemplateB)
})
The above code might be very hard to understand. It will take some time to familiarise yourself with Kotlin, what it does, and how it interfaces with TeamCity. I should point out that some imports are missing.
Additionally, take the example with a pinch of salt. It is a quick example to demonstrate one way of templates implementing other templates. Do not take this example as the definitive way to do things.
Unfortunately, this is currently not possible but already requested for a long time in TW-12153 (maybe you would like to vote for it).
To share several build steps among several build configurations or build configuration templates, I am using meta runners:
A Meta-Runner allows you to extract build steps, requirements and parameters from a build configuration and create a build runner out of them.
This build runner can then be used as any other build runner in a build step of any other build configuration or template.
Although using meta runners works as a workaround for us, editing meta runners is not as convenient as editing a build configuration template (as it usually requires editing the meta runner definition XML file by hand).
Update 2021
As #zosal points out in his answer TeamCity meanwhile provides another way of sharing common build configuration data or logic by means of the Kotlin DSL. The Kotlin DSL is a very powerful tool but may not always fit in your specific scenario. I would recommend to at least give it a try or watch one of the introductory tutorial videos.
Following on from this question.
If I have a build with two instances of the Test task, what is the best (cleanest, least code, most robust) way to completely separate those two tasks so that their outputs don't overlap?
I've tried setting their testResultsDir and testReportsDir properties, but that didn't seem to work as expected. (That is, the output got written to separate directories, but still the two tasks re-ran their respective tests with each run.)
Update for the current situation as of gradle 1.8: The testReportDir and reportsDir properties in dty's answer are deprecated since gradle 1.3. Test results are now separated automatically in the "test-results" directory and to set different destination directories for the HTML reports, call
tasks.withType(Test) {
reports.html.destination = file("${reporting.baseDir}/${name}")
}
Yet again, Rene has pointed me in the right direction. Thank you, Rene.
It turns out that this approach does work, but I must have been doing something wrong.
For reference, I added the following to my build after all the Test tasks had been defined:
tasks.withType(Test) {
testReportDir = new File("${reportsDir}/${testReportDirName}/${name}")
testResultsDir = new File("${buildDir}/${testResultsDirName}/${name}")
}
This will cause all instances of the Test task to be isolated from each other by having their task name as part of their directory hierarchy.
However, I still feel that this is a bit evil and there must be a cleaner way of achieving this that I haven't yet found!
Ingo Kegel's answer doesn't address the results directory, only the reports directory. Which means that a test report for a particular test type could be built that includes more test results than just that type. This can be addressed by setting the results directory as well.
tasks.withType(Test) {
reports.html.destination = file("${reporting.baseDir}/${name}")
reports.junitXml.destination = file("${testResultsDir}/${name}")
}
Just an update. The reports.html.destination way is deprecated.
This is the "new" way (Gradle > 4.x):
tasks.withType(Test) {
reports.html.setDestination file("${reporting.baseDir}/${name}")
}