Задача push-движка приложения всегда возвращает 404 в тесте

У меня есть очередь задач push в приложении Go App Engine. Когда мы пытаемся поставить задачи в очередь на тестирование по какой-либо причине, задачи всегда возвращают 404.

Наш app.yaml:

runtime: go
api_version: go1.9

handlers:
 - url: /worker/.*
   script: _go_app
   login: admin
 - url: /.*
   script: _go_app

Фактический вызов задачи:

func Handler(w http.ResponseWriter, r *http.Request) {
    ctx := appengine.NewContext(r)
    t := taskqueue.NewPOSTTask("/worker", map[string][]string{"key": {"val"}})
    _, err := taskqueue.Add(ctx, t, "")
    if err != nil {
        log.Errorf(ctx, "Failed to add task");
    }
    fmt.Fprintf(w, "Success");
}

Еще неполный обработчик, но он существует!

func Worker(w http.ResponseWriter, r *http.Request) {
    ctx := appengine.NewContext(r)
    log.Infof(ctx, "Worker succeeded")
}

и, наконец, доказательство того, что мы действительно добавили путь к нашему маршрутизатору:

func init() {
    http.HandleFunc("/", Handler)
    http.HandleFunc("/worker", Worker)
}

Когда мы на самом деле запускаем тесты, мы всегда получаем следующий вывод журнала:

INFO     2018-05-03 09:51:11,794 module.py:846] default: "POST /worker HTTP/1.1" 404 19
WARNING  2018-05-03 09:51:11,794 taskqueue_stub.py:2149] Task � failed to execute. This task will retry in 0.100 seconds
INFO     2018-05-03 09:51:11,897 module.py:846] default: "POST /worker HTTP/1.1" 404 19
WARNING  2018-05-03 09:51:11,897 taskqueue_stub.py:2149] Task � failed to execute. This task will retry in 0.200 seconds
INFO     2018-05-03 09:51:12,101 module.py:846] default: "POST /worker HTTP/1.1" 404 19
WARNING  2018-05-03 09:51:12,101 taskqueue_stub.py:2149] Task � failed to execute. This task will retry in 0.400 seconds

Обратите внимание, что конечная точка /worker возвращает 302, когда я пытаюсь пропинговать ее через клиент API, такой как Paw, поэтому маршрут, похоже, настроен правильно. 404 возникает только тогда, когда я пытаюсь запустить что-то в тесте.

Почему это возвращает 404? Я пробовал запускать тесты для примера очереди push в их документации столкнулся с той же проблемой - есть ли какой-то отсутствующий флаг конфигурации, который я не могу передать goapp?

Я выложил репозиторий GitHub с минимальным воспроизводимым примером здесь


person Venantius    schedule 03.05.2018    source источник
comment
Вы зарегистрировали свой mux? Как? Пожалуйста, покажите этот код.   -  person icza    schedule 03.05.2018
comment
Также api_version должно быть go1, это не версия Go.   -  person icza    schedule 03.05.2018
comment
go1.9 — это поддерживаемый api_version, но в настоящее время он находится на стадии бета-поддержки. См. cloud.google.com/appengine/docs/standard/ перейти/конфигурации/   -  person Venantius    schedule 03.05.2018
comment
Добавлен код, показывающий, где зарегистрирован mux.   -  person Venantius    schedule 03.05.2018
comment
Другие ваши обработчики работают? Если у вас нет других обработчиков, добавьте один для целей тестирования.   -  person icza    schedule 03.05.2018
comment
Все мои обычные обработчики HTTP работают нормально — у меня есть внешний интерфейс SPA, который проксирует их, и до сих пор не было никаких проблем. Я не включал их код, потому что у меня не было с ними никаких проблем, и я не хотел вносить путаницу. Они добавляются к одному и тому же мультиплексору таким же образом.   -  person Venantius    schedule 03.05.2018
comment
Также добавлено дополнительное примечание - только что попробовал конечную точку через Paw и получил 302, что указывает, по крайней мере, на то, что конфигурация app.yaml верна.   -  person Venantius    schedule 03.05.2018
comment
Что такое URL-адрес возврата для ответа 302?   -  person Tarun Lalwani    schedule 06.05.2018
comment
И это 404 локально? Если это так, это может быть ваша проблема   -  person Tarun Lalwani    schedule 06.05.2018
comment
@TarunLalwani извините, этот URL-адрес оказался неверным, потому что я настраивал маршруты, но да - независимо от того, что указано, это правильно, потому что этот URL-адрес автоматически генерируется App Engine, когда вы устанавливаете login: admin в app.yaml на определенном маршруте.   -  person Venantius    schedule 06.05.2018


Ответы (2)


Порядок запуска goapp сверху вниз, но вы должны быть конкретными в своем app.yaml. В вашем случае это будет работать:

package main

import (
    "fmt"
    "net/http"

    "google.golang.org/appengine"
    "google.golang.org/appengine/log"
    "google.golang.org/appengine/taskqueue"
)

func Handler(w http.ResponseWriter, r *http.Request) {
    ctx := appengine.NewContext(r)
    t := taskqueue.NewPOSTTask("/worker", map[string][]string{"key": {"val"}})
    _, err := taskqueue.Add(ctx, t, "")
    if err != nil {
        log.Errorf(ctx, "Failed to add task")
    }
    fmt.Fprintf(w, "Success")
}

func Worker(w http.ResponseWriter, r *http.Request) {
    ctx := appengine.NewContext(r)
    log.Infof(ctx, "Worker succeeded")
}

func init() {
    http.HandleFunc("/", Handler)
    http.HandleFunc("/worker", Worker)
}

Для этого вам нужно сопоставить URL-адрес, например:

runtime: go
api_version: go1.9

handlers:
 - url: /worker
   script: _go_app
   login: admin
 - url: /.*
   script: _go_app

Результат:

введите здесь описание изображения

Видите, что рабочий выполняется два раза. Это происходит потому, что GET /favicon.ico входит в сопоставление GET /.*. Так что это только детали для вас!

ОБНОВЛЕНИЕ (14.05.2018): В вашем тесте вы используете aetest.NewInstance(), который запускает dev_appserver.py в ioutil.TempDir("", "appengine-aetest"), который пишет ваши собственные main.go и app.yaml. См. выше в instance_vm.go:

i.appDir, err = ioutil.TempDir("", "appengine-aetest")
if err != nil {
    return err
}
defer func() {
    if err != nil {
        os.RemoveAll(i.appDir)
    }
}()
err = os.Mkdir(filepath.Join(i.appDir, "app"), 0755)
if err != nil {
    return err
}
err = ioutil.WriteFile(filepath.Join(i.appDir, "app", "app.yaml"), []byte(i.appYAML()), 0644)
  if err != nil {
    return err
}
err = ioutil.WriteFile(filepath.Join(i.appDir, "app", "stubapp.go"), []byte(appSource), 0644)
if err != nil {
    return err
}

//... others codes

const appYAMLTemplate = `
application: %s
version: 1
runtime: go
api_version: go1
vm: true
handlers:
- url: /.*
  script: _go_app
`

const appSource = `
package main
import "google.golang.org/appengine"
func main() { appengine.Main() }
`

Поэтому вам нужно создать свой собственный сервер-экземпляр. Это способ:

//out buffer
var out bytes.Buffer
//start server
c := exec.Command("goapp", "serve") //default port=8080, adminPort=8000
c.Stdout = &out
c.Stderr = &out
c.Start()
defer c.Process.Kill()
//delay to wait server is completed
time.Sleep(10 * time.Second)

//... others codes

//quit server
quitReq, err := http.NewRequest("GET", "http://localhost:8000/quit", nil)
_, err := client.Do(quitReq)
if err != nil {
    fmt.Errorf("GET /quit handler error: %v", err)
}

Чтобы проверить функцию Handler, выполните следующие действия:

//create request (testing Handler func)
req, err := http.NewRequest("GET", "http://localhost:8080/", nil)
if err != nil {
    t.Fatal(err.Error())
}
//do GET
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
    t.Error(err)
}
defer resp.Body.Close()
//delay to wait for the worker to execute
time.Sleep(10 * time.Second)

Чтобы получить результат и протестировать его:

//read response
b, _ := ioutil.ReadAll(resp.Body)
resp_content := string(b)
//checking
if !strings.Contains(resp_content, "Handler Success") {
    t.Errorf("Handler not working")
}
//log server content
logserver := out.String()
if !strings.Contains(logserver, "Worker succeeded") {
    t.Errorf("Worker not working")
}
//log response
t.Logf(logserver)

Результат:

скриншот - результат теста

ОБНОВЛЕНИЕ: ссылка на Github: https://github.com/ag-studies/go-appengine-sample

Надеюсь, это поможет!!

person Aristofanio Garcia    schedule 12.05.2018
comment
Можете ли вы предоставить репозиторий GitHub с рабочим примером? Код, который вы предоставили, все еще имеет ту же ошибку на моей машине, хотя мы могли бы написать наш тест по-другому. - person Venantius; 12.05.2018
comment
Обновлен со ссылкой на github. - person Aristofanio Garcia; 13.05.2018
comment
В этом репо, похоже, нет тестового файла. Этот вопрос конкретно касается запуска тестов. Были ли ваши журналы из тестов или просто из запуска сервера разработки? - person Venantius; 13.05.2018

Ваш шаблон handlers — это /worker/.*, но вы отдаете задачи /worker.

Вы должны сделать 1 из следующего:

  1. Либо измените шаблон на /worker.*, либо просто /worker
  2. Или отправьте запрос(ы) /worker/ или /worker/some-task-name.
person Alexander Trakhimenok    schedule 08.05.2018
comment
Вы зарегистрировали обработчик задач? Можете ли вы предоставить код для этого? - person Alexander Trakhimenok; 08.05.2018
comment
Весь код для регистрации обработчиков включен как в вопрос, так и в связанный репозиторий GitHub. - person Venantius; 08.05.2018
comment
Должно работать при работе с goapp serve. Что вы подразумеваете под запуском тестов? Вы имеете в виду aetest? Если это так, я думаю, что существует известная проблема с поддержкой очередей задач. Но goapp serve должно работать после исправления шаблона. - person Alexander Trakhimenok; 08.05.2018
comment
Да, это отлично работает с goapp serve. Я имею в виду использование aetest // goapp test. - person Venantius; 08.05.2018
comment
Я не смог найти никаких ссылок на известные проблемы с поддержкой очередей задач в Google (кроме поддержки пользовательских очередей задач, которая отличается от этой проблемы). - person Venantius; 08.05.2018
comment
Я видел вашу проблему github.com/golang/appengine/issues/134 и не понял, что это твое. Никогда не тестировал очереди с помощью aetes, но я помню, что для Python нам пришлось реализовать собственный хак и вызвать в тесте что-то вроде пользовательских process_queues. Так что я думаю, что это может быть похоже на Go. - person Alexander Trakhimenok; 08.05.2018