Bazel: ttl for an artefact - bazel

I am writing a bazel rule, and one of the steps is acquiring an authentication token that will expire in some time. When I rebuild this target after that time, the step sees that nothing regarding getting that token has changed, so bazel uses a cached token.
Is there a way to take the TTL of that token into account? Or at least force that step to be rebuilt every time the build is run?

The problem here is, that you actively want to write a rule that breaks bazels hermeticity guarantees.
I would advise to generate the authentication token outside of bazel and inject it into the build. There are several options to inject your secret:
using --action_env=SECRET=$TOKEN as a command-line argument (possibly via a generated .bazelrc). This has the downside of invalidating your entire bazel cache as every rule has to re-execute when the token changes.
generate a secret.bzl somehere containing a SECRET="..." line that you can load() where you need it.
If you don't want to generate the token outside of bazel, you can write a custom repository_rule() that generates a load()able file:
def _get_token_impl(repository_ctx):
repository_ctx.file(
"BUILD.bazel",
"",
executable = False,
)
repository_ctx.file(
"secret.bzl",
"SECRET = {}".format("..."),
executable = False,
)
get_token = repository_rule(
implementation = _get_token_impl,
local = True, # important
)
The local = True here is important:
Indicate that this rule fetches everything from the local system and should be reevaluated at every fetch.

Related

Terraform private endpoint and subsequent apply results in destroy/recreate

resource "azurerm_synapse_managed_private_endpoint" "example" {
name = "example-endpoint"
synapse_workspace_id = azurerm_synapse_workspace.example.id
target_resource_id = azurerm_storage_account.example_connect.id
subresource_name = "blob"
depends_on = [azurerm_synapse_firewall_rule.example]
}
Question 1# subsequent apply results in destroy/recreate and changing IPaddress. Resulting in production issue?
Question 2# How to auto approve blob storage endpoint ? "Approval State" is set to "Pending"
Question 1# subsequent apply results in destroy/recreate and changing
IPaddress. Resulting in production issue?
Subsequent apply for a terraform resource won't destroy and recreate the existince resource. Terraform works on incremental changes.As the configuration changes, Terraform is able to determine what changed and create incremental execution plans

Bazel fetch remote file not as a WORKSPACE rule?

In Bazel, how do I fetch a remote file as a build rule not as a WORKSPACE rule?
I want to use a build rule because WORKSPACE rules are not loaded for transitively.
e.g. this fails
load("#bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
http_file(
name = "foo",
urls = [ "https://example.com" ],
sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
executable = True,
)
Error in repository_rule: 'repository rule http_file' can only be called during workspace loading
If you really want to do that, you have to implement your own rule, a naïve trivial example relying on curl to fetch could be:
def _impl(ctx):
args = ctx.actions.args()
args.add("-o", ctx.outputs.out)
args.add(ctx.attr.url)
ctx.actions.run(
outputs = [ctx.outputs.out],
executable = "curl",
arguments = [args],
)
get_stuff = rule(
_impl,
attrs = {
"url": attr.string(
mandatory = True,
),
},
outputs = {"out": "%{name}.out"},
)
But (and esp. in such a trivial) for, it comes with problems. Apart from, do you want to step out of sandbox during the build? And do you want to talk to someone across the network during the build (out of the sandbox)? Bypassing repository_cache, and possibly getting remote_cache involved (networked caching of networked fetching). Specifically in this example, if content of the file pointed to by url changes... build has no idea and only fetches it when it either hasn't done so or the url itself has changed. I.e. the implementation would need to be more robust (mimic that of http_file for instance).
But it actually sounds like you're trying to address a different problem (transitive external dependencies, for which there could be another solution). One trick used for that is to define a macro (in your first level dependency to load define the next hop) and after declaring that first step as an external dependency in your parent project, load the that macro and use it from parent project WORKSPACE. This too has a price though, namely the first level dependency has to always be present (fetched or already cached), even if build target asked for does not actually need it (as that load and macro call will always pull it in).

Bazel external dependency rebuilt unnecessarily in rule used as a tool from within another rule

I am working on a set of Bazel rules where one test rule is also executed as a tool from within another executable rule. The test rule depends on an external tool which is built by rules_foreign_cc.
symbiyosys_test = rule(
implementation = _symbiyosys_test_impl,
doc = "Formal verification of (System) Verilog.",
attrs = {
...,
"_yosys_toolchain": attr.label(
doc = "Yosys toolchain.",
default = Label("#rules_symbiyosys//symbiyosys/tools:yosys"),
),
"_yices_toolchain": attr.label(
doc = "Yices toolchain.",
default = Label("#rules_symbiyosys//symbiyosys/tools:yices"),
),
},
test = True,
)
symbiyosys_trace = rule(
implementation = _symbiyosys_trace_impl,
doc = "View VCD trace from Symbiyosys.",
attrs = {
"test": attr.label(
doc = "Symbiyosys test target to produce VCD file.",
mandatory = True,
executable = True,
cfg = "exec",
),
...,
},
executable = True,
)
With a virgin Bazel cache, when an instance of the test rule is run with bazel test //examples:counter_fail the external tool is built. The external tool is also built when an instance of the executable rule (which utilizes the test rule) is run with bazel run //examples:counter_fail_trace. Once the external tool has been built in these two contexts, subsequent tests or runs use the cached outputs.
Building the external tool twice seems unnecessary as both the test and executable rule have the same configuration ("exec"). I have a hunch that this may have to do with bazel test and bazel run invoking different command line options causing the cache to miss on the external dependency.
My question is primarily what is causing this rebuild and how do I get rid of it? And short of answering that, what are some techniques to dig into what is causing this rebuild? I have tried some basic Bazel queries, but haven't had much luck.
EDIT
I still haven't cracked this one. I do suspect a subtle difference between bazel test and bazel run but unfortunately there is limited information about how specifically the two differ in the documentation.

PullRequest Build Validation with Jenkins and OnPrem Az-Devops

First off the setup in question:
A Jenkins Instance with several build nodes and on prem Azure-Devops server containing the Git Repositories.
The Repo in question is too large to always build on push for all branches and all devs, so a small workaround was done:
The production branches have a polling enabled twice a day (because of testing duration which is handled downstream more builds would not help with quality)
All other branches have their automated building suppressed. They still can start it manually for Builds/Deployments/Unittests if they so choose.
The jenkinsfile has parameterization for which platforms to build, on prod* all the platforms are true, on all other branches false.
This helps because else the initial build of a feature branch would always build/deploy locally all platforms which would take too much of a load on the server infrastructure.
I added a service endpoint for Jenkins in the Azure Devops, added a Buildvalidation .yml - this basically works because when I call the sourcebranch of the pull request with the merge commitID i added a parameter
isPullRequestBuild which contains the ID of the PR.
snippet of the yml:
- task: JenkinsQueueJob#2
inputs:
serverEndpoint: 'MyServerEndpoint'
jobName: 'MyJob'
isMultibranchJob: true
captureConsole: true
capturePipeline: true
isParameterizedJob: true
multibranchPipelineBranch: $(System.PullRequest.SourceBranch)
jobParameters: |
stepsToPerform=Build
runUnittest=true
pullRequestID=$(System.PullRequest.PullRequestId)
Snippet of the Jenkinsfile:
def isPullRequest = false
if ( params.pullRequestID?.trim() )
{
isPullRequest = true
//do stuff to change how the pipeline should react.
}
In the jenkinsfile I look whether the parameter is not empty and reset the platforms to build to basically all and to run the unittests.
The problem is: if the branch has never run, Jenkins does not already know the parameter in the first run, so it is ignored, building nothing, and returning with 0 because "nothing had to be done".
Is there any way to only run the jenkins build if it hasnt run already?
Or is it possible to get information from the remote call if this was the build with ID 1?
The only other thing would be to Call the Jenkins via web api and check for the last successful build, but in that case I would have have the token somewhere stored in source control.
Am I missing something obvious here? I dont want to trigger the feature branch builds to do nothing more than once, because Devs could lose useful information about their started builds/deployments.
Any ideas appreciated
To whom it may concern with similar problems:
In the end I used the following workaround:
The Jenkins Endpoint is called via a user that only is used for automated builds. So, in case that this user triggered the build, I set everything to run a Pull Request Validation, even if it is the first build. Along the lines of
def causes = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
if (causes != null)
{
def buildCauses= readJSON text: currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause').toString()
buildCauses.each
{
buildCause ->
if (buildCause['userId'] == "theNameOfMyBuildUser")
{
triggeredByAzureDevops = true
}
}
}
getBuildcauses must be allowed to run by a Jenkins Admin for that to work.

Bazel overwriting test logs and BuildEventProtocol via BuildEventService

I am playing around with implementing a server of the BuildEventService so that I can have bazel export its BuildEventProtocol messages to it. I'm trying to figure out how to read the logs for a test run without race conditions, and in particular this seems very difficult due to bazel reusing the same path on the local machine for multiple runs and the default asynchronous nature of BES.
Example:
As part of the event stream I get the following:
EventStream event:
stream_id {
build_id: "a4a34ca2-fc4b-483d-b4ab-b4546bdb2c4e"
component: TOOL
invocation_id: "b09c0b08-b096-4673-9521-4980506207f7"
}
sequence_number: 11
event {
event_time {
seconds: 1504560960
nanos: 778000000
}
bazel_event {
[type.googleapis.com/build_event_stream.BuildEvent] {
id {
test_summary {
label: "//libraries:types-test"
configuration {
id: "fe35dfece8e09ba054305e51187b3316"
}
}
}
test_summary {
total_run_count: 1
failed {
uri: "file:///private/var/tmp/_bazel_endobson/f851d7f6c7010ae7d7a3db153bed36de/execroot/yaspl/bazel-out/darwin_x86_64-fastbuild/testlogs/libraries/types-test/test.log"
}
overall_status: FAILED
}
}
}
}
I would like to read the file in the uri:
file:///private/var/tmp/_bazel_endobson/f851d7f6c7010ae7d7a3db153bed36de/execroot/yaspl/bazel-out/darwin_x86_64-fastbuild/testlogs/libraries/types-test/test.log
but it seems that every time I run the test I get the same uri. Thus I want to read it before the next test run recreates it. But bazel by default does the uploading asynchronously, so it seems there is nothing preventing another run of bazel of starting up and recreating the file even before the BES server receives this stream message.
How can I avoid this race and still read these files?
It depends on whether you are in control of the Bazel client. If so then yes you can avoid the race. Else you can't.
You can specify a different --output_base on each invocation of
Bazel (The output base is the path prefix
/private/var/tmp/_bazel_endobson/f851d7f6c7010ae7d7a3db153bed36de in
your example). However, that --output_base is a startup option and
thus requires a Bazel server restart when it's changed. That would
work but it's slow and you need to specify the different
--output_base before the invocation, which might be fine if you invoke Bazel programmatically.
You can specify --bes_best_effort=false in which case the BES upload
is synchronous i.e. Bazel waits for the upload to finish. If the
upload fails, the build also fails.
You could wrap the bazel client in a shell script and additionally to uploading to your BES service, also write the BEP to a file and then at the end of the invocation parse the file for test.log files and upload these before giving control back to the user.

Resources