-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conditional at Launch #38
base: main
Are you sure you want to change the base?
Conversation
a68009c
to
9a3a727
Compare
9a3a727
to
33100e5
Compare
I think we may need to explicitly exclude add the metadata server host to |
d1e647b
to
1c37f23
Compare
Previously, a binding was required at build time in order to contribute the layers for the Stackdriver components. We've started to think that requiring a binding during build is too onerous a restriction and instead environment variables should be used to indicate that a dependency should be contributed. The knock-on effect of this change is that we have to move a lot more of the conditional logic out to launch time including ensuring that if the binding doesn't exist _nothing_ is contributed. This change makes those updates to the buildpack's behavior. In addition, this change also includes an update that enables the buildpack to determine if the application is running in GCP by detecting the metadata server. In this case, no binding is required at launch time either as the credentials typically provided by the binding are instead provided by the metadata server. Finally, this includes a fix where the provided build plan wasn't anywhere near exhaustive for combinations that a user need when using the buildpack. Signed-off-by: Ben Hale <[email protected]>
1c37f23
to
300f282
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love the direction this is going! But I still have a couple questions.
| `$BP_GOOGLE_CLOUD_DEBUGGER_ENABLED` | Whether to add Google Cloud Debugger during build | ||
| `$BP_GOOGLE_CLOUD_PROFILER_ENABLED` | Whether to add Google Cloud Profiler during build | ||
| `$BPL_GOOGLE_CLOUD_MODULE` | Configure the name of the application (required) | ||
| `$BPL_GOOGLE_CLOUD_PROJECT_ID` | Configure the project id for the application (required if running outside of Google Cloud) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know I made the opposite comment earlier but you mentioned that the project ID is now included in the service account key and therefore is not actually required in all situations. If we wanted to be clever (maybe too clever) we could pull the project ID out of the key name
or project_id
fields https://cloud.google.com/iam/docs/creating-managing-service-account-keys.
### Type: `GoogleCloud` | ||
|Key | Value | Description | ||
|-------------------------|------------------|------------ | ||
|`ApplicationCredentials` | `<JSON Payload>` | Google Cloud Application Credentials in JSON form |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|`ApplicationCredentials` | `<JSON Payload>` | Google Cloud Application Credentials in JSON form | |
|`ApplicationCredentials` | `<JSON Payload>` | Google Cloud [Service Account Key][s] (JSON) | |
[s]: https://cloud.google.com/iam/docs/creating-managing-service-account-keys |
I think using the more specific terminology and a link will help folks understand the workflow more quickly.
Credentials = "google-cloud-credentials" | ||
DebuggerJava = "google-cloud-debugger-java" | ||
DebuggerNodeJS = "google-cloud-debugger-nodejs" | ||
ProfilerJava = "google-cloud-profiler-java" | ||
ProfilerNodeJS = "google-cloud-profiler-nodejs" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the plan entry constants are only used in the google
package. Why not move them there?
|
||
const ( | ||
Credentials = "google-cloud-credentials" | ||
DebuggerJava = "google-cloud-debugger-java" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like provide a common prefix for a set of related constants. What do you think about prefixing all plan entry constants with PlanEntry
(e.g. PlanEntryJavaDebugger
) so it is clear what the constant is for.
Other prefix suggestions:
Env*
(e.g.EnvModule
)Source*
(e.g.SourceBinding
)
) | ||
|
||
if hasMetadataServer() { | ||
c = common.MetadataServer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't we want the binding to take precedence over the metadata server?
Provides: []libcnb.BuildPlanProvide{ | ||
{Name: common.Credentials}, | ||
{Name: common.DebuggerJava}, | ||
{Name: common.ProfilerJava}, | ||
{Name: common.DebuggerNodeJS}, | ||
{Name: common.ProfilerNodeJS}, | ||
}, | ||
Requires: []libcnb.BuildPlanRequire{ | ||
{Name: common.Credentials}, | ||
{Name: common.DebuggerJava}, | ||
{Name: common.ProfilerJava}, | ||
{Name: "jvm-application"}, | ||
{Name: common.DebuggerNodeJS}, | ||
{Name: common.ProfilerNodeJS}, | ||
{Name: "node", Metadata: map[string]interface{}{"build": true}}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this more carefully, won't we always select this plan if both jvm-application
and node
are provided by other buildpacks, rendering the subsequent two plans in the debugger && profiler
conditional moot?
{Name: "jvm-application"}, | ||
{Name: common.DebuggerNodeJS}, | ||
{Name: common.ProfilerNodeJS}, | ||
{Name: "node", Metadata: map[string]interface{}{"build": true}}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is interesting.... if node
is provided by another buildpack but only at build time (i.e node is necessary at build time but the resulting app is not a NodeJS application we probably don't want to install the NodeJS profiler and debugger. However, the https://github.com/paketo-buildpacks/npm-start and https://github.com/paketo-buildpacks/yarn-start buildpacks don't seem to provide anything we can key off of that would be analogous to jvm-application
.
) | ||
|
||
func IsModuleRequired(module string, content []byte) (bool, error) { | ||
p := fmt.Sprintf(`require\(['"]%s['"]\)`, module) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what all the allowed characters are in a module name; is there any danger that special characters in the name could prevent the regex from compiling? Some alternatives would be:
- add a capture group and then compare the matched module names to
module
. - use two naive
strings.Contains
, one with"
s and one with'
s and return if either is true
t, err := template.New("require-module"). | ||
Parse(`require('{{.Module}}').start({ | ||
serviceContext: { | ||
service: '{{.Service}}', | ||
version: '{{.Version}}', | ||
}, | ||
}); | ||
`) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to parse template\n%w", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
t, err := template.New("require-module"). | |
Parse(`require('{{.Module}}').start({ | |
serviceContext: { | |
service: '{{.Service}}', | |
version: '{{.Version}}', | |
}, | |
}); | |
`) | |
if err != nil { | |
return nil, fmt.Errorf("unable to parse template\n%w", err) | |
} | |
t := template.Must(template.New("require-module"). | |
Parse(`require('{{.Module}}').start({ | |
serviceContext: { | |
service: '{{.Service}}', | |
version: '{{.Version}}', | |
}, | |
}); | |
`)) |
Given the template is constant it is safe to use a Must
here.
t, err := template.New("require-module-external"). | ||
Parse(`require('{{.Module}}').start({ | ||
projectId: '{{.ProjectId}}', | ||
serviceContext: { | ||
service: '{{.Service}}', | ||
version: '{{.Version}}', | ||
}, | ||
}); | ||
`) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to parse template\n%w", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
t, err := template.New("require-module-external"). | |
Parse(`require('{{.Module}}').start({ | |
projectId: '{{.ProjectId}}', | |
serviceContext: { | |
service: '{{.Service}}', | |
version: '{{.Version}}', | |
}, | |
}); | |
`) | |
if err != nil { | |
return nil, fmt.Errorf("unable to parse template\n%w", err) | |
} | |
t := template.Must(template.New("require-module-external"). | |
Parse(`require('{{.Module}}').start({ | |
projectId: '{{.ProjectId}}', | |
serviceContext: { | |
service: '{{.Service}}', | |
version: '{{.Version}}', | |
}, | |
}); | |
`)) |
Previously, a binding was required at build time in order to contribute the layers for the Stackdriver components. We've started to think that requiring a binding during build is too onerous a restriction and instead environment variables should be used to indicate that a dependency should be contributed. The knock-on effect of this change is that we have to move a lot more of the conditional logic out to launch time including ensuring that if the binding doesn't exist nothing is contributed. This change makes those updates to the buildpack's behavior.
In addition, this change also includes an update that enables the buildpack to determine if the application is running in GCP by detecting the metadata server. In this case, no binding is required at launch time either as the credentials typically provided by the binding are instead provided by the metadata server.
Finally, this includes a fix where the provided build plan wasn't anywhere near exhaustive for combinations that a user need when using the buildpack.
[#2]