I've been utilizing the url provided to me by the google-app-engine, i.e. "projectname-id.appspot.com" for my project. I had previously purchased a custom domain from GoDaddy, and I followed the steps at the following link to verify ownership of the domain name within the Google Cloud Platform, add the domain to my Google App Engine Project, and update my DNS settings in GoDaddy to point to the listed CNAME/server combination. https://cloud.google.com/appengine/docs/go/console/using-custom-domains-and-ssl
When I visit my custom domain, a website page is served, but it's always a 404. The "projectname-id.appspot.com" url still works correctly, and when I look at the log statements in the Google App Engine, it receives the request from both my custom domain and the appspot domain - which seems to suggest the domain dns was updated properly. See image below, where the 404 is from the custom domain and the 200 is from the appspot url:
Google App Engine Logs
Is there anything else I have to do? The backend is written in Go, and we're using the Mux Router. Do I need to modifiy my app.yaml file or edit my routes somehow? Any suggestions would be greatly appreciated.
Here I've included some code snippets I use to initialize my server:
App.yaml
version: alpha-001
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
env_variables:
PRODUCTION: 'TRUE'
DATASTORE_DATASET: 'app-id'
DATASTORE_HOST: 'http://localhost:8043'
DATASTORE_EMULATOR_HOST: 'localhost:8043'
DATASTORE_PROJECT_ID: 'app-id'
GOOGLE_APPLICATION_CREDENTIALS: './app-string.json'
Initializing the WebConsole (Server):
func init() {
// Web Server for API Endpoints
flag.Parse()
var server *web_console.WebConsole
if prod := os.Getenv("PRODUCTION"); prod == "FALSE" {
server = web_console.NewWebConsole(false)
} else {
server = web_console.NewWebConsole(true)
}
server.Run()
}
WebConsole.go
type WebConsole struct {
prod bool
Mux *mux.Router
DbMap *gorp.DbMap
Client *datastore.Client
}
func NewWebConsole(prod bool) *WebConsole {
return &WebConsole{
prod: prod,
}
}
func (w *WebConsole) Run() {
w.dbInit()
w.routesInit()
}
func (w *WebConsole) dbInit() {
// Configure SQL connection
// Code removed for privacy reasons
}
func (w *WebConsole) routesInit() {
// Configure routes with Mux
w.Mux = mux.NewRouter()
api.AddCertChallengeApis(w.Mux)
// The path "/" matches everything not matched by some other path.
// Checkout: http://stackoverflow.com/questions/26581231/google-cloud-go-handler-without-router-gorilla-mux
// for more details
http.Handle("/", w.Mux)
}
Api Package File for Routing
package api
import (
"github.com/gorilla/mux"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
"net/http"
"strings"
)
func AddCertChallengeApis(r *mux.Router) {
r.Schemes("http")
r.HandleFunc("/", defaultHandler())
}
func defaultHandler() http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
// Construct new app engine context
c := appengine.NewContext(req)
log.Infof(c, "App ID: %v", appengine.AppID(c))
rw.Header().Set("Content-Type", "text/plain")
rw.Write([]byte("hi, welcome to my website yo"))
log.Infof(c, "Hit the website")
}
}
I have done custom domains on appengine/go before, and there is nothing else you should have to do. I would try removing this line, though,
r.Schemes("http")
in case it is something https related.
Related
Goal: Use the MS Identity application that was created by Microsoft and try to tailor the application according to our needs.
Issue The android application is not matching the signature hash with the Applications' Reply URI.
Configuration Values
Signature Hash: ga0RGNYHvNM5d0SLGQfpQWAPGJ8=
Redirect URI: msauth://com.azuresamples.msalandroidapp/ga0RGNYHvNM5d0SLGQfpQWAPGJ8%3D
Android:host: com.azuresamples.msalandroidapp
Android Scheme: msauth
Logs and Screenshots
Logs show: The redirect URI in the configuration file doesn't match with the one generated with package name and signature hash.
Screenshot of the error in the Android Emulator
Methods Tried
Tried to fix this by changing the android:path value to /ga0RGNYHvNM5d0SLGQfpQWAPGJ8%3D however got this message from the UI:
Screenshot of the update android:path value
Tried to change the Reply URI directly from Azure Portal, but was not able to
Tried changing the Redirect URI in the android code to msauth://com.azuresamples.msalandroidapp/ga0RGNYHvNM5d0SLGQfpQWAPGJ8%3D but that gave the same error
The default library in the code was 1.2, changed it to 1.5+ in gradle and still it didnt work.
Configuration Files
auth_config_single_account.json
"client_id" : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"authorization_user_agent" : "DEFAULT",
"redirect_uri" : "msauth://com.azuresamples.msalandroidapp/ga0RGNYHvNM5d0SLGQfpQWAPGJ8%3D",
"account_mode" : "SINGLE",
"broker_redirect_uri_registered": true,
"authorities" : [
{
"type": "AAD",
"authority_url": "https://login.microsoftonline.com/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
]
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.azuresamples.msalandroidapp">
...
<intent-filter>
...
<data
android:host="com.azuresamples.msalandroidapp"
android:path="/ga0RGNYHvNM5d0SLGQfpQWAPGJ8="
android:scheme="msauth" />
</intent-filter>
...
</manifest>
Build-Gradle:app
...
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "androidx.appcompat:appcompat:$rootProject.ext.appCompatVersion"
implementation "com.google.android.material:material:$rootProject.ext.materialVersion"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.android.volley:volley:1.1.1'
if (findProject(':msal') != null) {
// For developer team only.
localImplementation project(':msal')
externalImplementation 'com.microsoft.identity.client:msal:1.5.+'
}
else {
// Downloads and Builds MSAL from maven central.
implementation 'com.microsoft.identity.client:msal:1.5.+'
}
}
Update
Was trying to avoid this but I have updated my keystore using the debug.keystore that microsoft provides and it works, however this is not solution for production environments. What are the steps I need to take for this to work in the production environment.
Not sure if it will be a relevant answer, but will share my experience.
At some point I run into the same issue, dig into the library and figured out how hash is calculated there. It was something like this (I have translated it into kotlin):
try {
val info = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
)
for (signature in info.signatures) {
val md = MessageDigest.getInstance("SHA")
md.update(signature.toByteArray())
val hash = Base64.encodeToString(
md.digest(),
Base64.DEFAULT
)
Log.d("KeyHash", "KeyHash:$hash")
}
} catch (e: PackageManager.NameNotFoundException) {
Log.d("KeyHash", e.toString())
} catch (e: NoSuchAlgorithmException) {
}
The value calculated this way didn't match the value from running this command:
keytool -exportcert -alias androiddebugkey -keystore %HOMEPATH%\.android\debug.keystore | openssl sha1 -binary | openssl base64
But it was the one that msal library expects to get. Same happened on release signature. I haven't yet investigated the reason (maybe I did something wrong), but for now I'm using signature hash got from the first method, and it works fine.
This is Dome from the MSAL Android team.
If you want to use your own keystore, you will have to register your own application.
com.azuresamples.msalandroidapp was only registered with the signature hash of the provided debug.keystore.
Please let me know if you have any other question. You can also leave a question in our GitHub repository as well (we're constantly monitoring that one).
I tried with cmd and keytool but not get success. Please Try this code to generate key hash programmatically in you code.
String packageName = getApplicationContext().getPackageName();
try {
PackageInfo info = getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md;
md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
sha1Singature = new String(Base64.encode(md.digest(), 0));
//String something = new String(Base64.encodeBytes(md.digest()));
Log.e(AppConstants.TAG, sha1Singature);
}
} catch (PackageManager.NameNotFoundException e1) {
Log.e("name not found", e1.toString());
} catch (NoSuchAlgorithmException e) {
Log.e("no such an algorithm", e.toString());
} catch (Exception e) {
Log.e("exception", e.toString());
}
and then you set this Signature hash to Azure portal.
The official tutorial from Ktor.io doesn't work, I tried it.
It's my first deploy. Thanks for help.
My gradle file (kts):
import com.google.cloud.tools.gradle.appengine.appyaml.AppEngineAppYamlExtension
buildscript {
dependencies {
classpath("com.google.cloud.tools:appengine-gradle-plugin:2.2.0")
}
}
apply {
plugin("com.google.cloud.tools.appengine")
}
plugins {
// Support for Kotlin
id("org.jetbrains.kotlin.jvm") version "1.3.61"
// Support for building a CLI application
application
// Documentation
id("org.jetbrains.dokka") version "0.10.1"
war
// id("com.improve_future.harmonica") version "1.1.24"
}
application {
mainClassName = "com.easythings.parkkometr.AppKt"
group = "com.easythings"
version = "0.0.1"
}
sourceSets {
main {
java.srcDir("app/main/src")
resources.srcDir("app/main/resources")
}
test {
java.srcDir("app/test/src/")
resources.srcDir("app/test/resources/")
}
}
repositories {
jcenter()
maven { setUrl("https://kotlin.bintray.com/ktor") }
}
dependencies {
val ktorVersion: String by project
val logbackVersion: String by project
val exposedVersion: String by project
val pgVersion: String by project
val spekVersion: String by project
val sendGridVersion: String by project
val assertJVersion: String by project
// Kotlin ============================================================================
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Libs ============================================================================
// Ktor - framework
implementation("io.ktor", "ktor-server-jetty", ktorVersion)
implementation("io.ktor", "ktor-server-core", ktorVersion)
implementation("io.ktor", "ktor-server-host-common", ktorVersion)
implementation("io.ktor", "ktor-auth", ktorVersion)
implementation("io.ktor", "ktor-auth-jwt", ktorVersion)
implementation("io.ktor", "ktor-gson", ktorVersion)
implementation("io.ktor", "ktor-network-tls-certificates", ktorVersion)
// Logback - application logger
implementation("ch.qos.logback", "logback-classic", logbackVersion)
// Exposed - orm
implementation("org.jetbrains.exposed", "exposed-core", exposedVersion)
implementation("org.jetbrains.exposed", "exposed-dao", exposedVersion)
implementation("org.jetbrains.exposed", "exposed-jdbc", exposedVersion)
implementation("org.jetbrains.exposed", "exposed-jodatime", exposedVersion)
// Postgresql - database driver
implementation("org.postgresql", "postgresql", pgVersion)
// SendGrid - mailer
implementation("com.sendgrid", "sendgrid-java", sendGridVersion)
// Deploy ============================================================================
providedCompile("com.google.appengine", "appengine", "1.9.60")
// Tests ============================================================================
}
tasks {
dokka {
outputDirectory = "$buildDir/docs/dokka"
outputFormat = "html"
}
test {
useJUnitPlatform {
includeEngines("spek2")
}
}
}
configure<AppEngineAppYamlExtension> {
deploy {
setAppEngineDirectory(".")
version = "1"
projectId = "XYZ-placeholder"
stopPreviousVersion = true // default - stop the current version
promote = true // default - & make this the current version
}
stage {
setAppEngineDirectory(".")
}
}
My app.yaml file:
runtime: java
env: flex
handlers:
- url: /.*
script: this field is required, but ignored
I get information that the deploy has finished successfully, but I get a 403 error when I call gcloud app browse.
HTTP ERROR 403 Problem accessing /. Reason:
Forbidden
I think that my app doesn't start and it's the error from Jetty, but I don't know how to check/confirm and fix it.
The task appengineRun doesn't exist in this version GAE.
First of all, the provided Ktor tutorial for App Engine Standard environment, but you have "flex" env in the app.yaml file.
Also I would like to recommend you follow the official more informative Google Cloud documentation for Ktor.
I've been developing with Google App Engine Go for awhile, but I haven't touched it in a year. I tried updating my application from "go1" (1.9?) to "go111", and I'm currently getting some weird errors without any explanation as to what's going on.
The errors include:
The request failed because the instance could not start successfully
Container called exit(1).
500 Internal server error
etc.
None of these point me to any specific line in my code where something would go wrong, nor explain anything more meaningful...
I'm guessing the error is stemming from me upgrading between the golang versions. I had to change the app package into main, add a main function to the application, update the appengine package to a newer version, update the gsuite app, add a cloud compilation widget thingy, change app.yaml script from go to auto, etc.
All in all, I'm lost. A Similar SE question yielded no good answers. Someone else suggested app.yaml might be at fault, so here is mine:
runtime: go111
handlers:
- url: /static
static_dir: static
- url: /res
static_dir: res
- url: /update
script: auto
secure: always
login: admin
- url: /.*
script: auto
secure: always
login: optional
Debug console log is very unhelpful:
And the main file looks essentially like:
package main
import (
"fmt"
"google.golang.org/appengine"
"html/template"
"log"
"net/http"
)
var MainTemplate *template.Template
func main() {
http.HandleFunc("/", hello)
var err error
MainTemplate, err = template.ParseFiles("html/main.html")
if err != nil {
log.Printf("ERROR! %v\n", err.Error())
panic(err)
}
log.Printf("Hello world!\n")
}
func hello(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
//................................//
MainTemplate.Execute(w, nil)
}
Anything else it could be?
Okay, after some help from a few comments, here are a few issues that I had to fix to get my code to work:
I converted my init() function into main() without adding a way to listen to requests. The code just ran through the main() function and exited without an error, hence the problems with debugging.
appengine.NewContext(r) is deprecated apparently, so I had to switch those statements to r.Context(). MEANWHILE, Appengine Datastore is still using golang.org/x/net/context and not just context, so if you want to use things like RunInTransaction(), DON'T update your imports, context casts into golang.org/x/net/context just fine
If you follow the official examples provided by Google, you will most likely run into errors like textPayload: "2019/10/20 22:32:46 http: panic serving 127.0.0.1:16051: not an App Engine context. Instead, your code needs to look like the below example.
Since the former app package is now the main package, make sure that any files that are referenced in app.yaml (favicon.ico for example) are in a proper position in relation to the main package (I had to move mine to a different folder to avoid errors popping up every request...).
package main
import (
"google.golang.org/appengine"
"html/template"
"net/http"
)
var MainTemplate *template.Template
func init() {
http.HandleFunc("/", hello)
MainTemplate, _= template.ParseFiles("html/main.html")
}
func main() {
appengine.Main()
}
func hello(w http.ResponseWriter, r *http.Request) {
c := r.Context()
//................................//
MainTemplate.Execute(w, nil)
}
This ought to work. appengine.Main() apparently connects you to all the appengine functionality needed to use the context for datastore / memcache / whatever operations.
Thank you to the commenters that helped me get over the first hump!
Your code is missing the part to start a listener. Add this to your code:
port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
There's a problem where I don't know why a context.Context was changed once I pass it to a different package on Google App Engine.
The following code works fine when running on App Engine:
package main
import (
"net/http"
"log"
"google.golang.org/appengine"
)
func main() {
http.HandleFunc("/", myHandler)
appengine.Main()
}
func myHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
account, err := appengine.ServiceAccount(ctx)
if err != nil {
log.Println("[myHandler] error:", err)
} else {
log.Println("[myHandler] ServiceAccount:", account)
}
w.Write([]byte("ok"))
}
I could retrieve the ServiceAccount successfully when accessing /, and everything was good.
However, when I passed the context from main.go to another package, the function call didn't work. The following was added to main.go:
import (
// other stuff
"github.com/adlerhsieh/q_context/handlers"
)
func main() {
http.HandleFunc("/", myHandler)
appengine.Main()
}
func myHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
account, err := appengine.ServiceAccount(ctx)
if err != nil {
log.Println("[myHandler] error:", err)
} else {
log.Println("[myHandler] ServiceAccount:", account)
}
handlers.AnotherFunc(ctx) // <--- added this
w.Write([]byte("ok"))
}
Another package:
package handlers
import (
"log"
"context"
"google.golang.org/appengine"
)
func AnotherFunc(ctx context.Context) {
account, err := appengine.ServiceAccount(ctx)
if err != nil {
log.Println("[AnotherFunc] error:", err)
} else {
log.Println("[AnotherFunc] ServiceAccount:", account)
}
}
When I ran it on App Engine, the log said:
2019/09/04 09:36:30 [myHandler] ServiceAccount: myaccount#gmail.com
2019/09/04 09:36:30 [AnotherFunc] error: not an App Engine context
The function calls are the same, but just in different packages. I dug in the package itself and found that it uses the key here (which leads to here) to setup the context. And here to check whether that value was setup properly. However, that value seem to be modified/changed so that the second function call couldn't get it. Even if I omitted the first function call and went straight to the second one, it still has the same error.
Any idea why context object was modified when passing to another package?
The following is my app.yaml:
runtime: go111
service: default
instance_class: F1
automatic_scaling:
min_idle_instances: 0
max_idle_instances: automatic
min_pending_latency: automatic
max_pending_latency: automatic
max_concurrent_requests: 30
handlers:
- url: /.*
script: auto
login: admin
nobuild_files:
- vendor
env_variables:
ENV: 'dev'
GO111MODULE: 'off'
Here is the GitHub repo link.
Thank you!
It turns out that my code actually worked. It's because of some other operation error.
However, I'll just post the issue that actually caused it so it can help those who have the same issue.
With the new go111 runtime, it treats packages from non-root directory or its subdirectories as a different type of package. This caused the problem with "not an App Engine context". I'll just call it an "outcast" package for now (cause I'm not entirely sure why's that).
For example:
- appengine
- main.go
- handlers
- handlers.go <-- this is a regular package
- appengine
- main.go
- handlers
- handlers.go < -- this is an outcast package
An outcast package would have issues handling context.Context generated from App Engine, as pointed out in my question.
The mechanism of App Engine knowing that the context is created from App Engine, is using a built-in value that can only be retrieved from its internal package (with an un-exported pointer-string key). When passing the context to an outcast package, we can no longer retrieve the value from the context. It's still a mystery for me that why the value disappeared, but it's probably because of some Go compiling mechanism.
The solution would be moving the main.go to the top-level directory in the project, so that there would be no outcast package anywhere.
I am trying to test an AppEngine/Go application. I start dev_appserver.py and it begins serving the application, but when I go to localhost:8080 in my browser, I get:
Compile error:
/home/adam/foobar/server/app/server.go:5: can't find import: appengine/users
2011/08/23 19:45:34 go-app-builder: Failed building app: failed running 8g: exit status 1
I feel as though I need to do something to make the AppEngine-specific libraries available where GO expects them to be, but I don't really want to run goinstall on everything that comes in the AppEngine/Go SDK zip, do I? I seem to have missed an installation step, but for the life of me, I can't figure the sane and right thing to do.
I am on Ubuntu, if that matters.
The Users API isn't appengine/users - it's appengine/user. From the example on the App Engine page:
import (
"appengine"
"appengine/user"
)
func welcome(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
u := user.Current(c)
if u == nil {
url := u.LoginURL(c, "/")
fmt.Fprintf(w, `Sign in or register`, url)
return
}
url := user.LogoutURL(c, "/")
fmt.Fprintf(w, `Welcome, %s! (sign out)`, u, url)
}
You don't have to compile the code yourself - just run the dev_appserver and it will compile it for you whenever the code changes. Have you gone through the getting started docs?