Compare commits
No commits in common. "master" and "master" have entirely different histories.
|
@ -1,59 +0,0 @@
|
|||
version: 2
|
||||
name: aly-prod
|
||||
description: 阿里云生产环境
|
||||
global:
|
||||
concurrent: 1
|
||||
cache:
|
||||
- GOCACHE
|
||||
- GOMODCACHE
|
||||
workflow:
|
||||
- ref: start
|
||||
name: 开始
|
||||
task: start
|
||||
- ref: git_clone_0
|
||||
name: git clone
|
||||
task: git_clone@1.2.9
|
||||
input:
|
||||
remote_url: '"https://gitlink.org.cn/JointCloud/pcm-ac.git"'
|
||||
ref: '"refs/heads/master"'
|
||||
commit_id: '""'
|
||||
depth: 1
|
||||
needs:
|
||||
- start
|
||||
- ref: docker_image_build_0
|
||||
name: docker镜像构建
|
||||
cache:
|
||||
GOCACHE: /root/.cache/go-build
|
||||
GOMODCACHE: /go/pkg/mod
|
||||
task: docker_image_build@1.6.0
|
||||
input:
|
||||
docker_username: ((aly.docker_user))
|
||||
docker_password: ((aly.docker_password))
|
||||
image_name: '"registry.cn-hangzhou.aliyuncs.com/jcce/pcm-ac"'
|
||||
image_tag: '"latest"'
|
||||
registry_address: '"registry.cn-hangzhou.aliyuncs.com"'
|
||||
docker_file: '"Dockerfile"'
|
||||
docker_build_path: '"."'
|
||||
workspace: git_clone_0.git_path
|
||||
image_clean: true
|
||||
image_push: true
|
||||
build_args: '""'
|
||||
needs:
|
||||
- git_clone_0
|
||||
- ref: ssh_cmd_0
|
||||
name: ssh执行命令
|
||||
task: ssh_cmd@1.1.1
|
||||
input:
|
||||
ssh_private_key: ((aly.ssh_private_key))
|
||||
ssh_ip: '"47.92.39.128"'
|
||||
ssh_port: '"22"'
|
||||
ssh_user: '"root"'
|
||||
ssh_cmd: '"kubectl rollout restart deployment -n ns-admin pcm-ac"'
|
||||
needs:
|
||||
- docker_image_build_0
|
||||
- ref: end
|
||||
name: 结束
|
||||
task: end
|
||||
needs:
|
||||
- ssh_cmd_0
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
version: 2
|
||||
name: dev
|
||||
description: ""
|
||||
global:
|
||||
concurrent: 1
|
||||
param:
|
||||
- ref: nacos_host
|
||||
name: nacos_host
|
||||
value: '"10.206.0.12"'
|
||||
required: false
|
||||
type: STRING
|
||||
hidden: false
|
||||
- ref: secret_name
|
||||
name: ""
|
||||
value: '"jcce-aliyuncs"'
|
||||
required: false
|
||||
type: STRING
|
||||
hidden: false
|
||||
- ref: project_name
|
||||
name: ""
|
||||
value: '"pcm-ac"'
|
||||
required: false
|
||||
type: STRING
|
||||
hidden: false
|
||||
trigger:
|
||||
webhook: gitlink@1.0.0
|
||||
event:
|
||||
- ref: create_tag
|
||||
ruleset:
|
||||
- param-ref: tag
|
||||
operator: EQ
|
||||
value: '""'
|
||||
ruleset-operator: AND
|
||||
workflow:
|
||||
- ref: start
|
||||
name: 开始
|
||||
task: start
|
||||
- ref: git_clone_0
|
||||
name: git clone
|
||||
task: git_clone@1.2.6
|
||||
input:
|
||||
remote_url: '"https://gitlink.org.cn/JointCloud/pcm-ac.git"'
|
||||
ref: '"refs/heads/master"'
|
||||
commit_id: '""'
|
||||
depth: 1
|
||||
needs:
|
||||
- start
|
||||
- ref: docker_image_build_0
|
||||
name: docker镜像构建
|
||||
task: docker_image_build@1.6.0
|
||||
input:
|
||||
docker_username: ((dev.docker_user))
|
||||
docker_password: ((dev.docker_password))
|
||||
image_name: '"registry.cn-hangzhou.aliyuncs.com/jcce/pcm-ac"'
|
||||
image_tag: git_clone_0.commit_time
|
||||
registry_address: '"registry.cn-hangzhou.aliyuncs.com"'
|
||||
docker_build_path: git_clone_0.git_path
|
||||
workspace: git_clone_0.git_path
|
||||
image_clean: true
|
||||
image_push: true
|
||||
build_args: '""'
|
||||
needs:
|
||||
- shell_0
|
||||
- ref: end
|
||||
name: 结束
|
||||
task: end
|
||||
needs:
|
||||
- kubectl_deploy_0
|
||||
- ref: kubectl_deploy_0
|
||||
name: kubectl部署资源
|
||||
task: kubectl_deploy@1.1.0
|
||||
input:
|
||||
command: '"apply"'
|
||||
resource_file_path: git_clone_0.git_path
|
||||
certificate_authority_data: ((dev.k8s_cad))
|
||||
server: '"https://119.45.100.73:6443"'
|
||||
client_certificate_data: ((dev.k8s_ccd))
|
||||
client_key_data: ((dev.k8s_ckd))
|
||||
hosts: '""'
|
||||
needs:
|
||||
- docker_image_build_0
|
||||
- ref: shell_0
|
||||
name: shell
|
||||
image: docker.jianmuhub.com/library/debian:buster-slim
|
||||
env:
|
||||
IMAGE_NAME: '"registry.cn-hangzhou.aliyuncs.com/jcce/pcm-ac"'
|
||||
IMAGE_TAG: git_clone_0.commit_time
|
||||
SECRET_NAME: global.secret_name
|
||||
NACOS_HOST: global.nacos_host
|
||||
PROJECT_NAME: global.project_name
|
||||
PROJECT_PATH: git_clone_0.git_path
|
||||
script:
|
||||
- cd ${PROJECT_PATH}
|
||||
- sed -i "s#image_name#${IMAGE_NAME}:${IMAGE_TAG}#" ${PROJECT_NAME}.yaml
|
||||
- sed -i "s#secret_name#${SECRET_NAME}#" ${PROJECT_NAME}.yaml
|
||||
- sed -i "s#nacos_host#${NACOS_HOST}#" ${PROJECT_NAME}.yaml
|
||||
- cat ${PROJECT_NAME}.yaml
|
||||
needs:
|
||||
- git_clone_0
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
version: 2
|
||||
name: tencent-develop
|
||||
description: 腾讯云开发环境
|
||||
global:
|
||||
concurrent: 1
|
||||
cache:
|
||||
- GOCACHE
|
||||
- GOMODCACHE
|
||||
workflow:
|
||||
- ref: start
|
||||
name: 开始
|
||||
task: start
|
||||
- ref: git_clone_0
|
||||
name: git clone
|
||||
task: git_clone@1.2.9
|
||||
input:
|
||||
remote_url: '"https://gitlink.org.cn/JointCloud/pcm-ac.git"'
|
||||
ref: '"refs/heads/master"'
|
||||
commit_id: '""'
|
||||
depth: 1
|
||||
needs:
|
||||
- start
|
||||
- ref: docker_image_build_0
|
||||
name: docker镜像构建
|
||||
cache:
|
||||
GOCACHE: /root/.cache/go-build
|
||||
GOMODCACHE: /go/pkg/mod
|
||||
task: docker_image_build@1.6.0
|
||||
input:
|
||||
docker_username: ((aly.docker_user))
|
||||
docker_password: ((aly.docker_password))
|
||||
image_name: '"registry.cn-hangzhou.aliyuncs.com/jcce/pcm-ac"'
|
||||
image_tag: '"dev-latest"'
|
||||
registry_address: '"registry.cn-hangzhou.aliyuncs.com"'
|
||||
docker_file: '"Dockerfile"'
|
||||
docker_build_path: '"."'
|
||||
workspace: git_clone_0.git_path
|
||||
image_clean: true
|
||||
image_push: true
|
||||
build_args: '""'
|
||||
needs:
|
||||
- git_clone_0
|
||||
- ref: ssh_cmd_0
|
||||
name: ssh执行命令
|
||||
task: ssh_cmd@1.1.1
|
||||
input:
|
||||
ssh_private_key: ((aly.ssh_private_key))
|
||||
ssh_ip: '"119.45.255.234"'
|
||||
ssh_port: '"22"'
|
||||
ssh_user: '"root"'
|
||||
ssh_cmd: '"kubectl rollout restart deployment -n jcce-system pcm-ac"'
|
||||
needs:
|
||||
- docker_image_build_0
|
||||
- ref: end
|
||||
name: 结束
|
||||
task: end
|
||||
needs:
|
||||
- ssh_cmd_0
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
workflow_dispatch: # 允许手动触发
|
||||
|
||||
env:
|
||||
REGISTRY: registry.cn-hangzhou.aliyuncs.com # 修改为你的阿里云镜像仓库地址
|
||||
IMAGE_NAME: jcce/pcm-ac # 修改为你的阿里云镜像仓库名称
|
||||
IMAGE_TAG: latest
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ secrets.ALIYUN_USERNAME }}
|
||||
password: ${{ secrets.ALIYUN_PASSWORD }}
|
||||
|
||||
- name: Get commit ID and build time
|
||||
id: get_tags
|
||||
run: |
|
||||
echo "::set-output name=build_time::$(date +'%Y%m%d%H%M%S')"
|
||||
|
||||
- name: Build and push multi-arch image
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.get_tags.outputs.build_time }} \
|
||||
--push .
|
||||
|
||||
- name: Set up Kubernetes CLI
|
||||
uses: azure/setup-kubectl@v1
|
||||
|
||||
- name: Configure kubeconfig
|
||||
run: |
|
||||
mkdir -p ~/.kube
|
||||
echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config
|
||||
|
||||
- name: Restart Deployment
|
||||
run: kubectl rollout restart deployment ${{ secrets.SSH_DEPLOYMENT }}
|
|
@ -1,27 +0,0 @@
|
|||
name: Sync Mirror Repository
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 */8 * * *' # 每小时同步一次
|
||||
workflow_dispatch: # 允许手动触发
|
||||
|
||||
jobs:
|
||||
mirror:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
timeout-minutes: 10 # 设置作业的超时时间为10分钟
|
||||
|
||||
steps:
|
||||
- name: Checkout target repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1 # 获取完整的提交历史
|
||||
|
||||
- name: Mirror source repository
|
||||
uses: wearerequired/git-mirror-action@v1
|
||||
with:
|
||||
source-repo: "https://gitlink.org.cn/JointCloud/pcm-ac.git" # 源仓库的URL
|
||||
destination-repo: "git@github.com:${{ github.repository }}.git" # 目标仓库的URL
|
||||
ssh: true
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
33
Dockerfile
33
Dockerfile
|
@ -1,35 +1,30 @@
|
|||
FROM docker-0.unsee.tech/golang:alpine AS builder
|
||||
FROM golang:1.21.2-alpine3.18 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV GOPROXY=https://goproxy.cn \
|
||||
GO111MODULE=on \
|
||||
CGO_ENABLED=0 \
|
||||
GOCACHE=/root/.cache/go-build \
|
||||
GOMODCACHE=/go/pkg/mod
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RUN go mod download
|
||||
LABEL stage=gobuilder
|
||||
ENV CGO_ENABLED 0
|
||||
ENV GOARCH amd64
|
||||
ENV GOPROXY https://goproxy.cn,direct
|
||||
|
||||
COPY . .
|
||||
COPY etc/ /app/
|
||||
RUN go build -o /app/pcm-ac /app/hpcac.go
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
# 使用 GOOS 和 GOARCH 环境变量来构建不同架构的二进制文件
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-w -s" -o pcm-ac
|
||||
|
||||
FROM --platform=$TARGETPLATFORM docker-0.unsee.tech/alpine:latest
|
||||
FROM alpine:3.18
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache ca-certificates && update-ca-certificates && \
|
||||
#修改alpine源为上海交通大学
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.sjtug.sjtu.edu.cn/g' /etc/apk/repositories && \
|
||||
apk update && \
|
||||
apk upgrade && \
|
||||
apk add --no-cache ca-certificates && update-ca-certificates && \
|
||||
apk add --update tzdata && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
COPY --from=builder /app/pcm-ac .
|
||||
COPY --from=builder /app/etc/hpcac.yaml ./etc/
|
||||
COPY etc/hpcac.yaml /app/etc/
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
|
|
|
@ -46,33 +46,23 @@ ParastorQuotaUrl : "/hpc/openapi/v2/parastor/quota/usernames/{username}" #查询
|
|||
# 曙光Ai 相关配置
|
||||
BaseUrlAi : "https://ac.sugon.com"
|
||||
GetImageListAi : "/sothisai/api/tasks/training/images"
|
||||
GetInstanceAi : "/sothisai/api/instance-service"
|
||||
GetTaskAi : "/sothisai/api/{taskGroup}/training/tasks/{taskId}"
|
||||
SubmitTaskAi : "/sothisai/api/{taskGroup}/training/tasks"
|
||||
DeleteTaskAi : "/sothisai/api/tasks"
|
||||
GetResourceSpec : "/sothisai/api/tasks/resources"
|
||||
GetInstanceLog : "/sothisai/api/tasks/{taskId}/instances/worker/{instanceNum}/log"
|
||||
StopTaskAi: "/sothisai/api/tasks/{taskId}/actions/stop"
|
||||
|
||||
# 作业
|
||||
GetQueueNames: "/hpc/openapi/v2/queuenames/users/{username}" #查询用户可访问队列
|
||||
GetJobList: "/hpc/openapi/v2/jobs"
|
||||
GetJobDetail: "/hpc/openapi/v2/jobs/{jobId}"
|
||||
|
||||
# 文件
|
||||
GetFileList : "/openapi/v2/file/list"
|
||||
GetFile: "/openapi/v2/file/download"
|
||||
UploadFile : "/openapi/v2/file/upload"
|
||||
ExistFile : "/openapi/v2/file/exist"
|
||||
|
||||
# 容器
|
||||
GetInstanceAi : "/sothisai/api/instance-service"
|
||||
GetNodeResources: "/ai/openapi/v2/instance-service/resources"
|
||||
GetInstanceServiceList: "/ai/openapi/v2/instance-service/task"
|
||||
GetInstanceServiceDetail: "/ai/openapi/v2/instance-service/{id}/detail"
|
||||
StartInstanceService: "/ai/openapi/v2/instance-service/task/actions/restart"
|
||||
StopInstanceService: "/ai/openapi/v2/instance-service/task/actions/stop"
|
||||
CreateInstanceService: "/ai/openapi/v2/instance-service/task"
|
||||
GetImageList: "/ai/openapi/v2/instance-service/images"
|
||||
|
||||
# 用户资源
|
||||
GetUserInfo : "/ac/openapi/v2/user"
|
||||
|
|
14
go.mod
14
go.mod
|
@ -17,9 +17,9 @@ require (
|
|||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/zeromicro/go-zero v1.6.2
|
||||
gitlink.org.cn/JointCloud/pcm-coordinator v0.0.0-20240301032931-d8d63c116dda
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/crypto v0.18.0
|
||||
google.golang.org/grpc v1.61.0
|
||||
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002
|
||||
google.golang.org/protobuf v1.32.0
|
||||
k8s.io/apimachinery v0.29.1
|
||||
k8s.io/client-go v0.29.1
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
|
@ -44,7 +44,7 @@ require (
|
|||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
|
@ -70,7 +70,6 @@ require (
|
|||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.4.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.12 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.12 // indirect
|
||||
|
@ -88,11 +87,10 @@ require (
|
|||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.26.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/oauth2 v0.15.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
|
|
34
go.sum
34
go.sum
|
@ -71,8 +71,8 @@ github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
|||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
|
@ -182,17 +182,15 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
|
@ -244,8 +242,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
|
@ -261,8 +259,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -271,8 +269,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -288,15 +286,15 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
|
@ -334,8 +332,8 @@ google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
|
|||
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002 h1:V7Da7qt0MkY3noVANIMVBk28nOnijADeOR3i5Hcvpj4=
|
||||
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
|
5047
hpcAC/hpcAC.pb.go
5047
hpcAC/hpcAC.pb.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
// Code generated by goctl. DO NOT EDIT.
|
||||
// Source: hpcAC.proto
|
||||
|
||||
package hpcacclient
|
||||
package hpcACClient
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -13,142 +13,118 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
ACClusterData = hpcAC.ACClusterData
|
||||
ACClusterReq = hpcAC.ACClusterReq
|
||||
ACClusterResp = hpcAC.ACClusterResp
|
||||
ACTokenData = hpcAC.ACTokenData
|
||||
ACTokenReq = hpcAC.ACTokenReq
|
||||
ACTokenResp = hpcAC.ACTokenResp
|
||||
ACTokenState = hpcAC.ACTokenState
|
||||
Children = hpcAC.Children
|
||||
ClusterResp = hpcAC.ClusterResp
|
||||
ContainerPortInfo = hpcAC.ContainerPortInfo
|
||||
ContainerPortInfoList = hpcAC.ContainerPortInfoList
|
||||
CpResp = hpcAC.CpResp
|
||||
CpuCore = hpcAC.CpuCore
|
||||
CpuCoreReq = hpcAC.CpuCoreReq
|
||||
CpuCoreResp = hpcAC.CpuCoreResp
|
||||
CreateInstanceServiceReq = hpcAC.CreateInstanceServiceReq
|
||||
CreateInstanceServiceResp = hpcAC.CreateInstanceServiceResp
|
||||
CreateParams = hpcAC.CreateParams
|
||||
DeleteJobReq = hpcAC.DeleteJobReq
|
||||
DeleteJobResp = hpcAC.DeleteJobResp
|
||||
DeleteTaskAiReq = hpcAC.DeleteTaskAiReq
|
||||
DeleteTaskAiResp = hpcAC.DeleteTaskAiResp
|
||||
Exist = hpcAC.Exist
|
||||
FileContentResp = hpcAC.FileContentResp
|
||||
FileDataReq = hpcAC.FileDataReq
|
||||
FileDataResp = hpcAC.FileDataResp
|
||||
FileList = hpcAC.FileList
|
||||
FileListData = hpcAC.FileListData
|
||||
GetFileListReq = hpcAC.GetFileListReq
|
||||
GetFileListResp = hpcAC.GetFileListResp
|
||||
GetFileReq = hpcAC.GetFileReq
|
||||
GetFileResp = hpcAC.GetFileResp
|
||||
GetImageAiByIdReq = hpcAC.GetImageAiByIdReq
|
||||
GetImageAiByIdResp = hpcAC.GetImageAiByIdResp
|
||||
GetImageListAiReq = hpcAC.GetImageListAiReq
|
||||
GetImageListAiResp = hpcAC.GetImageListAiResp
|
||||
GetImageListReq = hpcAC.GetImageListReq
|
||||
GetImageListResp = hpcAC.GetImageListResp
|
||||
GetInferUrlReq = hpcAC.GetInferUrlReq
|
||||
GetInferUrlResp = hpcAC.GetInferUrlResp
|
||||
GetInstanceDetailParams = hpcAC.GetInstanceDetailParams
|
||||
GetInstanceDetailResp = hpcAC.GetInstanceDetailResp
|
||||
GetInstanceListParams = hpcAC.GetInstanceListParams
|
||||
GetInstanceListReq = hpcAC.GetInstanceListReq
|
||||
GetInstanceListResp = hpcAC.GetInstanceListResp
|
||||
GetInstanceLogReq = hpcAC.GetInstanceLogReq
|
||||
GetInstanceLogResp = hpcAC.GetInstanceLogResp
|
||||
GetInstanceServiceDetailReq = hpcAC.GetInstanceServiceDetailReq
|
||||
GetInstanceServiceDetailResp = hpcAC.GetInstanceServiceDetailResp
|
||||
GetInstanceServiceListReq = hpcAC.GetInstanceServiceListReq
|
||||
GetInstanceServiceListReqParam = hpcAC.GetInstanceServiceListReqParam
|
||||
GetInstanceServiceListResp = hpcAC.GetInstanceServiceListResp
|
||||
GetJobDetailResp = hpcAC.GetJobDetailResp
|
||||
GetMemberJobsData = hpcAC.GetMemberJobsData
|
||||
GetMemberJobsReq = hpcAC.GetMemberJobsReq
|
||||
GetMemberJobsResp = hpcAC.GetMemberJobsResp
|
||||
GetNodeResourcesData = hpcAC.GetNodeResourcesData
|
||||
GetNodeResourcesReq = hpcAC.GetNodeResourcesReq
|
||||
GetNodeResourcesResp = hpcAC.GetNodeResourcesResp
|
||||
GetPytorchTaskReq = hpcAC.GetPytorchTaskReq
|
||||
GetPytorchTaskResp = hpcAC.GetPytorchTaskResp
|
||||
GetPytorchTaskRespParams = hpcAC.GetPytorchTaskRespParams
|
||||
GetResourceSpecReq = hpcAC.GetResourceSpecReq
|
||||
GetResourceSpecResp = hpcAC.GetResourceSpecResp
|
||||
GetTensorflowTaskReq = hpcAC.GetTensorflowTaskReq
|
||||
GetTensorflowTaskResp = hpcAC.GetTensorflowTaskResp
|
||||
GetTensorflowTaskRespParams = hpcAC.GetTensorflowTaskRespParams
|
||||
GetUserInfoData = hpcAC.GetUserInfoData
|
||||
GetUserInfoReq = hpcAC.GetUserInfoReq
|
||||
GetUserInfoResp = hpcAC.GetUserInfoResp
|
||||
GiResp = hpcAC.GiResp
|
||||
HistoryJobData = hpcAC.HistoryJobData
|
||||
HistoryJobDetail = hpcAC.HistoryJobDetail
|
||||
HistoryJobDetailReq = hpcAC.HistoryJobDetailReq
|
||||
HistoryJobDetailResp = hpcAC.HistoryJobDetailResp
|
||||
HistoryJobList = hpcAC.HistoryJobList
|
||||
ImageAI = hpcAC.ImageAI
|
||||
InstanceService = hpcAC.InstanceService
|
||||
IsExistFileReq = hpcAC.IsExistFileReq
|
||||
IsExistFileResp = hpcAC.IsExistFileResp
|
||||
Job = hpcAC.Job
|
||||
JobCore = hpcAC.JobCore
|
||||
JobDetail = hpcAC.JobDetail
|
||||
JobDetailReq = hpcAC.JobDetailReq
|
||||
JobInitAttr = hpcAC.JobInitAttr
|
||||
JobManager = hpcAC.JobManager
|
||||
JobManagerReq = hpcAC.JobManagerReq
|
||||
JobVncSessionInfo = hpcAC.JobVncSessionInfo
|
||||
JobsReq = hpcAC.JobsReq
|
||||
JobsResp = hpcAC.JobsResp
|
||||
ListHistoryJobReq = hpcAC.ListHistoryJobReq
|
||||
ListHistoryJobResp = hpcAC.ListHistoryJobResp
|
||||
ListJobManagerResp = hpcAC.ListJobManagerResp
|
||||
ListJobReq = hpcAC.ListJobReq
|
||||
ListJobResp = hpcAC.ListJobResp
|
||||
LogData = hpcAC.LogData
|
||||
MapAppJobInfo = hpcAC.MapAppJobInfo
|
||||
Metric = hpcAC.Metric
|
||||
MountInfo = hpcAC.MountInfo
|
||||
MountInfoList = hpcAC.MountInfoList
|
||||
ParaStorQuotaReq = hpcAC.ParaStorQuotaReq
|
||||
ParaStorQuotaResp = hpcAC.ParaStorQuotaResp
|
||||
PermissionAction = hpcAC.PermissionAction
|
||||
Queue = hpcAC.Queue
|
||||
QueueData = hpcAC.QueueData
|
||||
QueueDetailsData = hpcAC.QueueDetailsData
|
||||
QueueDetailsResp = hpcAC.QueueDetailsResp
|
||||
QueueJobsReq = hpcAC.QueueJobsReq
|
||||
QueueJobsResp = hpcAC.QueueJobsResp
|
||||
QueueReq = hpcAC.QueueReq
|
||||
QueueResp = hpcAC.QueueResp
|
||||
QuotaData = hpcAC.QuotaData
|
||||
ResourceReq = hpcAC.ResourceReq
|
||||
ResourceSpec = hpcAC.ResourceSpec
|
||||
StartInstanceServiceReq = hpcAC.StartInstanceServiceReq
|
||||
StartInstanceServiceResp = hpcAC.StartInstanceServiceResp
|
||||
StopInstanceServiceReq = hpcAC.StopInstanceServiceReq
|
||||
StopInstanceServiceResp = hpcAC.StopInstanceServiceResp
|
||||
StopTaskAiData = hpcAC.StopTaskAiData
|
||||
StopTaskAiReq = hpcAC.StopTaskAiReq
|
||||
StopTaskAiResp = hpcAC.StopTaskAiResp
|
||||
SubmitJobReq = hpcAC.SubmitJobReq
|
||||
SubmitJobResp = hpcAC.SubmitJobResp
|
||||
SubmitPytorchTaskParams = hpcAC.SubmitPytorchTaskParams
|
||||
SubmitPytorchTaskReq = hpcAC.SubmitPytorchTaskReq
|
||||
SubmitTaskAiResp = hpcAC.SubmitTaskAiResp
|
||||
SubmitTensorflowTaskParams = hpcAC.SubmitTensorflowTaskParams
|
||||
SubmitTensorflowTaskReq = hpcAC.SubmitTensorflowTaskReq
|
||||
TokenResp = hpcAC.TokenResp
|
||||
UploadFileData = hpcAC.UploadFileData
|
||||
UploadFileReq = hpcAC.UploadFileReq
|
||||
UploadFileResp = hpcAC.UploadFileResp
|
||||
UserQuotasLimitData = hpcAC.UserQuotasLimitData
|
||||
UserQuotasLimitResp = hpcAC.UserQuotasLimitResp
|
||||
WallTimeReq = hpcAC.WallTimeReq
|
||||
WallTimeResp = hpcAC.WallTimeResp
|
||||
ACClusterData = hpcAC.ACClusterData
|
||||
ACClusterReq = hpcAC.ACClusterReq
|
||||
ACClusterResp = hpcAC.ACClusterResp
|
||||
ACTokenData = hpcAC.ACTokenData
|
||||
ACTokenReq = hpcAC.ACTokenReq
|
||||
ACTokenResp = hpcAC.ACTokenResp
|
||||
ACTokenState = hpcAC.ACTokenState
|
||||
Children = hpcAC.Children
|
||||
ClusterResp = hpcAC.ClusterResp
|
||||
ContainerPortInfo = hpcAC.ContainerPortInfo
|
||||
CpResp = hpcAC.CpResp
|
||||
CpuCore = hpcAC.CpuCore
|
||||
CpuCoreReq = hpcAC.CpuCoreReq
|
||||
CpuCoreResp = hpcAC.CpuCoreResp
|
||||
DeleteJobReq = hpcAC.DeleteJobReq
|
||||
DeleteJobResp = hpcAC.DeleteJobResp
|
||||
DeleteTaskAiReq = hpcAC.DeleteTaskAiReq
|
||||
DeleteTaskAiResp = hpcAC.DeleteTaskAiResp
|
||||
FileContentResp = hpcAC.FileContentResp
|
||||
FileDataReq = hpcAC.FileDataReq
|
||||
FileDataResp = hpcAC.FileDataResp
|
||||
FileList = hpcAC.FileList
|
||||
FileListData = hpcAC.FileListData
|
||||
GetFileListReq = hpcAC.GetFileListReq
|
||||
GetFileListResp = hpcAC.GetFileListResp
|
||||
GetFileReq = hpcAC.GetFileReq
|
||||
GetFileResp = hpcAC.GetFileResp
|
||||
GetImageAiByIdReq = hpcAC.GetImageAiByIdReq
|
||||
GetImageAiByIdResp = hpcAC.GetImageAiByIdResp
|
||||
GetImageListAiReq = hpcAC.GetImageListAiReq
|
||||
GetImageListAiResp = hpcAC.GetImageListAiResp
|
||||
GetInstanceDetailParams = hpcAC.GetInstanceDetailParams
|
||||
GetInstanceDetailResp = hpcAC.GetInstanceDetailResp
|
||||
GetInstanceListParams = hpcAC.GetInstanceListParams
|
||||
GetInstanceListReq = hpcAC.GetInstanceListReq
|
||||
GetInstanceListResp = hpcAC.GetInstanceListResp
|
||||
GetInstanceLogReq = hpcAC.GetInstanceLogReq
|
||||
GetInstanceLogResp = hpcAC.GetInstanceLogResp
|
||||
GetJobDetailResp = hpcAC.GetJobDetailResp
|
||||
GetMemberJobsData = hpcAC.GetMemberJobsData
|
||||
GetMemberJobsReq = hpcAC.GetMemberJobsReq
|
||||
GetMemberJobsResp = hpcAC.GetMemberJobsResp
|
||||
GetNodeResourcesData = hpcAC.GetNodeResourcesData
|
||||
GetNodeResourcesReq = hpcAC.GetNodeResourcesReq
|
||||
GetNodeResourcesResp = hpcAC.GetNodeResourcesResp
|
||||
GetPytorchTaskReq = hpcAC.GetPytorchTaskReq
|
||||
GetPytorchTaskResp = hpcAC.GetPytorchTaskResp
|
||||
GetPytorchTaskRespParams = hpcAC.GetPytorchTaskRespParams
|
||||
GetResourceSpecReq = hpcAC.GetResourceSpecReq
|
||||
GetResourceSpecResp = hpcAC.GetResourceSpecResp
|
||||
GetTensorflowTaskReq = hpcAC.GetTensorflowTaskReq
|
||||
GetTensorflowTaskResp = hpcAC.GetTensorflowTaskResp
|
||||
GetTensorflowTaskRespParams = hpcAC.GetTensorflowTaskRespParams
|
||||
GetUserInfoData = hpcAC.GetUserInfoData
|
||||
GetUserInfoReq = hpcAC.GetUserInfoReq
|
||||
GetUserInfoResp = hpcAC.GetUserInfoResp
|
||||
GiResp = hpcAC.GiResp
|
||||
HistoryJobData = hpcAC.HistoryJobData
|
||||
HistoryJobDetail = hpcAC.HistoryJobDetail
|
||||
HistoryJobDetailReq = hpcAC.HistoryJobDetailReq
|
||||
HistoryJobDetailResp = hpcAC.HistoryJobDetailResp
|
||||
HistoryJobList = hpcAC.HistoryJobList
|
||||
ImageAI = hpcAC.ImageAI
|
||||
Job = hpcAC.Job
|
||||
JobCore = hpcAC.JobCore
|
||||
JobDetail = hpcAC.JobDetail
|
||||
JobDetailReq = hpcAC.JobDetailReq
|
||||
JobInitAttr = hpcAC.JobInitAttr
|
||||
JobManager = hpcAC.JobManager
|
||||
JobManagerReq = hpcAC.JobManagerReq
|
||||
JobVncSessionInfo = hpcAC.JobVncSessionInfo
|
||||
JobsReq = hpcAC.JobsReq
|
||||
JobsResp = hpcAC.JobsResp
|
||||
ListHistoryJobReq = hpcAC.ListHistoryJobReq
|
||||
ListHistoryJobResp = hpcAC.ListHistoryJobResp
|
||||
ListJobManagerResp = hpcAC.ListJobManagerResp
|
||||
ListJobReq = hpcAC.ListJobReq
|
||||
ListJobResp = hpcAC.ListJobResp
|
||||
LogData = hpcAC.LogData
|
||||
MapAppJobInfo = hpcAC.MapAppJobInfo
|
||||
Metric = hpcAC.Metric
|
||||
ParaStorQuotaReq = hpcAC.ParaStorQuotaReq
|
||||
ParaStorQuotaResp = hpcAC.ParaStorQuotaResp
|
||||
PermissionAction = hpcAC.PermissionAction
|
||||
Queue = hpcAC.Queue
|
||||
QueueData = hpcAC.QueueData
|
||||
QueueDetailsData = hpcAC.QueueDetailsData
|
||||
QueueDetailsResp = hpcAC.QueueDetailsResp
|
||||
QueueJobsReq = hpcAC.QueueJobsReq
|
||||
QueueJobsResp = hpcAC.QueueJobsResp
|
||||
QueueReq = hpcAC.QueueReq
|
||||
QueueResp = hpcAC.QueueResp
|
||||
QuotaData = hpcAC.QuotaData
|
||||
ResourceReq = hpcAC.ResourceReq
|
||||
ResourceSpec = hpcAC.ResourceSpec
|
||||
SubmitImageInferTaskReq = hpcAC.SubmitImageInferTaskReq
|
||||
SubmitImageInferTaskResp = hpcAC.SubmitImageInferTaskResp
|
||||
SubmitJobReq = hpcAC.SubmitJobReq
|
||||
SubmitJobResp = hpcAC.SubmitJobResp
|
||||
SubmitPytorchTaskParams = hpcAC.SubmitPytorchTaskParams
|
||||
SubmitPytorchTaskReq = hpcAC.SubmitPytorchTaskReq
|
||||
SubmitTaskAiResp = hpcAC.SubmitTaskAiResp
|
||||
SubmitTensorflowTaskParams = hpcAC.SubmitTensorflowTaskParams
|
||||
SubmitTensorflowTaskReq = hpcAC.SubmitTensorflowTaskReq
|
||||
TokenResp = hpcAC.TokenResp
|
||||
UploadFileData = hpcAC.UploadFileData
|
||||
UploadFileReq = hpcAC.UploadFileReq
|
||||
UploadFileResp = hpcAC.UploadFileResp
|
||||
UserQuotasLimitData = hpcAC.UserQuotasLimitData
|
||||
UserQuotasLimitResp = hpcAC.UserQuotasLimitResp
|
||||
WallTimeReq = hpcAC.WallTimeReq
|
||||
WallTimeResp = hpcAC.WallTimeResp
|
||||
|
||||
HpcAC interface {
|
||||
// ListJob list all jobs
|
||||
|
@ -196,27 +172,19 @@ type (
|
|||
GetPytorchTask(ctx context.Context, in *GetPytorchTaskReq, opts ...grpc.CallOption) (*GetPytorchTaskResp, error)
|
||||
GetTensorflowTask(ctx context.Context, in *GetTensorflowTaskReq, opts ...grpc.CallOption) (*GetTensorflowTaskResp, error)
|
||||
DeleteTaskAi(ctx context.Context, in *DeleteTaskAiReq, opts ...grpc.CallOption) (*DeleteTaskAiResp, error)
|
||||
StopTaskAi(ctx context.Context, in *StopTaskAiReq, opts ...grpc.CallOption) (*StopTaskAiResp, error)
|
||||
GetResourceSpec(ctx context.Context, in *GetResourceSpecReq, opts ...grpc.CallOption) (*GetResourceSpecResp, error)
|
||||
GetInstanceLog(ctx context.Context, in *GetInstanceLogReq, opts ...grpc.CallOption) (*GetInstanceLogResp, error)
|
||||
GetInstanceListAi(ctx context.Context, in *GetInstanceListReq, opts ...grpc.CallOption) (*GetInstanceListResp, error)
|
||||
GetInferUrl(ctx context.Context, in *GetInferUrlReq, opts ...grpc.CallOption) (*GetInferUrlResp, error)
|
||||
SubmitImageInferTask(ctx context.Context, in *SubmitImageInferTaskReq, opts ...grpc.CallOption) (*SubmitImageInferTaskResp, error)
|
||||
// 曙光文件接口
|
||||
GetFileList(ctx context.Context, in *GetFileListReq, opts ...grpc.CallOption) (*GetFileListResp, error)
|
||||
GetFile(ctx context.Context, in *GetFileReq, opts ...grpc.CallOption) (*GetFileResp, error)
|
||||
UploadFile(ctx context.Context, in *UploadFileReq, opts ...grpc.CallOption) (*UploadFileResp, error)
|
||||
IsExistFile(ctx context.Context, in *IsExistFileReq, opts ...grpc.CallOption) (*IsExistFileResp, error)
|
||||
// 用户资源
|
||||
GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error)
|
||||
GetMemberJobs(ctx context.Context, in *GetMemberJobsReq, opts ...grpc.CallOption) (*GetMemberJobsResp, error)
|
||||
// 容器
|
||||
// 获取节点资源限额
|
||||
GetNodeResources(ctx context.Context, in *GetNodeResourcesReq, opts ...grpc.CallOption) (*GetNodeResourcesResp, error)
|
||||
GetInstanceServiceList(ctx context.Context, in *GetInstanceServiceListReq, opts ...grpc.CallOption) (*GetInstanceServiceListResp, error)
|
||||
GetInstanceServiceDetail(ctx context.Context, in *GetInstanceServiceDetailReq, opts ...grpc.CallOption) (*GetInstanceServiceDetailResp, error)
|
||||
StartInstanceService(ctx context.Context, in *StartInstanceServiceReq, opts ...grpc.CallOption) (*StartInstanceServiceResp, error)
|
||||
StopInstanceService(ctx context.Context, in *StopInstanceServiceReq, opts ...grpc.CallOption) (*StopInstanceServiceResp, error)
|
||||
CreateInstanceService(ctx context.Context, in *CreateInstanceServiceReq, opts ...grpc.CallOption) (*CreateInstanceServiceResp, error)
|
||||
GetImageList(ctx context.Context, in *GetImageListReq, opts ...grpc.CallOption) (*GetImageListResp, error)
|
||||
}
|
||||
|
||||
defaultHpcAC struct {
|
||||
|
@ -383,11 +351,6 @@ func (m *defaultHpcAC) DeleteTaskAi(ctx context.Context, in *DeleteTaskAiReq, op
|
|||
return client.DeleteTaskAi(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) StopTaskAi(ctx context.Context, in *StopTaskAiReq, opts ...grpc.CallOption) (*StopTaskAiResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.StopTaskAi(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) GetResourceSpec(ctx context.Context, in *GetResourceSpecReq, opts ...grpc.CallOption) (*GetResourceSpecResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.GetResourceSpec(ctx, in, opts...)
|
||||
|
@ -403,9 +366,9 @@ func (m *defaultHpcAC) GetInstanceListAi(ctx context.Context, in *GetInstanceLis
|
|||
return client.GetInstanceListAi(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) GetInferUrl(ctx context.Context, in *GetInferUrlReq, opts ...grpc.CallOption) (*GetInferUrlResp, error) {
|
||||
func (m *defaultHpcAC) SubmitImageInferTask(ctx context.Context, in *SubmitImageInferTaskReq, opts ...grpc.CallOption) (*SubmitImageInferTaskResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.GetInferUrl(ctx, in, opts...)
|
||||
return client.SubmitImageInferTask(ctx, in, opts...)
|
||||
}
|
||||
|
||||
// 曙光文件接口
|
||||
|
@ -424,11 +387,6 @@ func (m *defaultHpcAC) UploadFile(ctx context.Context, in *UploadFileReq, opts .
|
|||
return client.UploadFile(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) IsExistFile(ctx context.Context, in *IsExistFileReq, opts ...grpc.CallOption) (*IsExistFileResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.IsExistFile(ctx, in, opts...)
|
||||
}
|
||||
|
||||
// 用户资源
|
||||
func (m *defaultHpcAC) GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
|
@ -440,38 +398,8 @@ func (m *defaultHpcAC) GetMemberJobs(ctx context.Context, in *GetMemberJobsReq,
|
|||
return client.GetMemberJobs(ctx, in, opts...)
|
||||
}
|
||||
|
||||
// 容器
|
||||
// 获取节点资源限额
|
||||
func (m *defaultHpcAC) GetNodeResources(ctx context.Context, in *GetNodeResourcesReq, opts ...grpc.CallOption) (*GetNodeResourcesResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.GetNodeResources(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) GetInstanceServiceList(ctx context.Context, in *GetInstanceServiceListReq, opts ...grpc.CallOption) (*GetInstanceServiceListResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.GetInstanceServiceList(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) GetInstanceServiceDetail(ctx context.Context, in *GetInstanceServiceDetailReq, opts ...grpc.CallOption) (*GetInstanceServiceDetailResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.GetInstanceServiceDetail(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) StartInstanceService(ctx context.Context, in *StartInstanceServiceReq, opts ...grpc.CallOption) (*StartInstanceServiceResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.StartInstanceService(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) StopInstanceService(ctx context.Context, in *StopInstanceServiceReq, opts ...grpc.CallOption) (*StopInstanceServiceResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.StopInstanceService(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) CreateInstanceService(ctx context.Context, in *CreateInstanceServiceReq, opts ...grpc.CallOption) (*CreateInstanceServiceResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.CreateInstanceService(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultHpcAC) GetImageList(ctx context.Context, in *GetImageListReq, opts ...grpc.CallOption) (*GetImageListResp, error) {
|
||||
client := hpcAC.NewHpcACClient(m.cli.Conn())
|
||||
return client.GetImageList(ctx, in, opts...)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"github.com/zeromicro/go-zero/core/conf"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/config"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/pkg/utils/httputils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
as = NewAuthService()
|
||||
)
|
||||
|
||||
type AuthService struct {
|
||||
C *config.Config
|
||||
Token string //区域用户认证token
|
||||
|
@ -19,91 +21,35 @@ type AuthService struct {
|
|||
JobManagerId int
|
||||
GroupId string
|
||||
QueueId string
|
||||
ResourceGroupAvail bool
|
||||
UserQueueAvail bool
|
||||
}
|
||||
|
||||
type ClusterTokens struct {
|
||||
ClusterId string
|
||||
ClusterName string
|
||||
Token string
|
||||
}
|
||||
|
||||
func NewAuthService() (map[string]*AuthService, error) {
|
||||
func NewAuthService() *AuthService {
|
||||
cfg := config.Config{}
|
||||
configFile := flag.String("c", "etc/hpcac.yaml", "the config file")
|
||||
conf.MustLoad(*configFile, &cfg)
|
||||
flag.Parse()
|
||||
clusterTokens, err := GetClusterTokens(&cfg)
|
||||
if err != nil {
|
||||
return nil, errors.New("区域用户认证失败")
|
||||
token, clusterId := getTokenAndClusterId(&cfg)
|
||||
return &AuthService{
|
||||
C: &cfg,
|
||||
Token: token,
|
||||
ClusterId: clusterId,
|
||||
AiCenterUrlPrefix: getAiCenterUrl(&cfg, token),
|
||||
HpcCenterUrlPrefix: getHpcCenterUrl(&cfg, token),
|
||||
EFileUrlPrefix: getEFileUrlPrefix(&cfg, token),
|
||||
JobManagerId: getJobManagerId(&cfg, token),
|
||||
GroupId: getGroupId(&cfg, token),
|
||||
QueueId: getQueueId(&cfg, token),
|
||||
}
|
||||
tokenMap := make(map[string]*AuthService)
|
||||
|
||||
for _, u := range clusterTokens {
|
||||
cid := u.ClusterId
|
||||
token := u.Token
|
||||
ctr, err := GetCenterUrls(cfg.BaseUrlAi, cfg.CenterUrl, token)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
auth := &AuthService{
|
||||
C: &cfg,
|
||||
Token: token,
|
||||
ClusterId: cid,
|
||||
AiCenterUrlPrefix: GetAiCenterUrl(ctr),
|
||||
HpcCenterUrlPrefix: GetHpcCenterUrl(ctr),
|
||||
EFileUrlPrefix: GetEFileUrlPrefix(ctr),
|
||||
JobManagerId: GetJobManagerId(&cfg, ctr, token),
|
||||
GroupId: GetGroupId(cfg.AcBaseUrl, cfg.UserConf.GetGroupMembers, token),
|
||||
QueueId: GetQueueId(&cfg, ctr, token),
|
||||
ResourceGroupAvail: IsAiResourceGroupAvail(ctr, cfg.AiResourceGroupUrl, token),
|
||||
UserQueueAvail: IsHpcUserQueueAvail(GetQueueId(&cfg, ctr, token)),
|
||||
}
|
||||
tokenMap[cid] = auth
|
||||
}
|
||||
if len(tokenMap) == 0 {
|
||||
return nil, errors.New("区域用户认证失败")
|
||||
}
|
||||
_, found := tokenMap[CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("区域用户认证失败")
|
||||
}
|
||||
return tokenMap, nil
|
||||
}
|
||||
|
||||
func GetAuthServiceWithResource(asv map[string]*AuthService) (*AuthService, error) {
|
||||
for _, service := range asv {
|
||||
if service.ResourceGroupAvail {
|
||||
return service, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("no available group resources")
|
||||
}
|
||||
|
||||
func (a *AuthService) update() error {
|
||||
clusters, err := GetClusterTokens(a.C)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range clusters {
|
||||
if a.ClusterId == c.ClusterId {
|
||||
a.Token = c.Token
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("update failed")
|
||||
func (a *AuthService) reGet() {
|
||||
a.Token, a.ClusterId = getTokenAndClusterId(a.C)
|
||||
}
|
||||
|
||||
func (a *AuthService) valid() bool {
|
||||
if a.Token == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
tokenStateUrl := a.C.AiConf.BaseUrlAi + a.C.TokenStateUrl
|
||||
var ts TokenState
|
||||
req := GetRestyRequest(TIMEOUT)
|
||||
req := GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", a.Token).
|
||||
SetResult(&ts).
|
||||
|
@ -116,53 +62,45 @@ func (a *AuthService) valid() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (a *AuthService) GetToken() string {
|
||||
if !a.valid() {
|
||||
err := a.update()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return a.Token
|
||||
func GetToken() string {
|
||||
if !as.valid() {
|
||||
as.reGet()
|
||||
}
|
||||
return a.Token
|
||||
return as.Token
|
||||
}
|
||||
|
||||
func (a *AuthService) GetConfig() *config.Config {
|
||||
return a.C
|
||||
func GetQueueId() string {
|
||||
return as.QueueId
|
||||
}
|
||||
|
||||
func (a *AuthService) GetQueueId() string {
|
||||
return a.QueueId
|
||||
func GetGroupId() string {
|
||||
return as.GroupId
|
||||
}
|
||||
|
||||
func (a *AuthService) GetGroupId() string {
|
||||
return a.GroupId
|
||||
func GetClusterId() string {
|
||||
return as.ClusterId
|
||||
}
|
||||
|
||||
func (a *AuthService) GetClusterId() string {
|
||||
return a.ClusterId
|
||||
func GetJobManagerId() int {
|
||||
return as.JobManagerId
|
||||
}
|
||||
|
||||
func (a *AuthService) GetJobManagerId() int {
|
||||
return a.JobManagerId
|
||||
func AiCenterUrlPrefix() string {
|
||||
return as.AiCenterUrlPrefix
|
||||
}
|
||||
|
||||
func (a *AuthService) GetAiCenterUrlPrefix() string {
|
||||
return a.AiCenterUrlPrefix
|
||||
func HpcCenterUrlPrefix() string {
|
||||
return as.HpcCenterUrlPrefix
|
||||
}
|
||||
|
||||
func (a *AuthService) GetHpcCenterUrlPrefix() string {
|
||||
return a.HpcCenterUrlPrefix
|
||||
func EFileUrlPrefix() string {
|
||||
return as.EFileUrlPrefix
|
||||
}
|
||||
|
||||
func (a *AuthService) GetEFileUrlPrefix() string {
|
||||
return a.EFileUrlPrefix
|
||||
}
|
||||
|
||||
func GetClusterTokens(cfg *config.Config) ([]*ClusterTokens, error) {
|
||||
func getTokenAndClusterId(cfg *config.Config) (string, string) {
|
||||
authUrl := cfg.AiConf.BaseUrlAi + cfg.AuthUrl
|
||||
var tr TokenResp
|
||||
req := GetRestyRequest(TIMEOUT)
|
||||
req := GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("user", cfg.User).
|
||||
SetHeader("password", cfg.Password).
|
||||
|
@ -171,78 +109,85 @@ func GetClusterTokens(cfg *config.Config) ([]*ClusterTokens, error) {
|
|||
Post(authUrl)
|
||||
|
||||
if err != nil || tr.Code != "0" {
|
||||
return nil, errors.New("区域用户认证失败")
|
||||
return "", ""
|
||||
}
|
||||
|
||||
var userTokens []*ClusterTokens
|
||||
for _, datum := range tr.Data {
|
||||
if datum.ClusterId != "0" {
|
||||
u := &ClusterTokens{
|
||||
ClusterId: datum.ClusterId,
|
||||
ClusterName: datum.ClusterName,
|
||||
Token: datum.Token,
|
||||
}
|
||||
userTokens = append(userTokens, u)
|
||||
return datum.Token, datum.ClusterId
|
||||
}
|
||||
}
|
||||
|
||||
if len(userTokens) == 0 {
|
||||
return nil, errors.New("可用区域为空")
|
||||
}
|
||||
|
||||
return userTokens, nil
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func GetCenterUrls(baseUrlAi string, centerUrl string, token string) (*Center, error) {
|
||||
url := baseUrlAi + centerUrl
|
||||
var cr Center
|
||||
req := GetRestyRequest(TIMEOUT)
|
||||
func getAiCenterUrl(cfg *config.Config, token string) string {
|
||||
centerUrl := cfg.AiConf.BaseUrlAi + cfg.CenterUrl
|
||||
var cr CenterResp
|
||||
req := GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetResult(&cr).
|
||||
Get(url)
|
||||
Get(centerUrl)
|
||||
|
||||
if err != nil || cr.Code != "0" {
|
||||
return nil, errors.New("get centerurl error")
|
||||
return ""
|
||||
}
|
||||
|
||||
return &cr, nil
|
||||
}
|
||||
|
||||
func GetAiCenterUrl(ctr *Center) string {
|
||||
for _, u := range ctr.Data.AiUrls {
|
||||
if u.Enable == "true" {
|
||||
//as.AiCenterUrlPrefix = url.Url
|
||||
return u.Url
|
||||
for _, url := range cr.Data.AiUrls {
|
||||
if url.Enable == "true" {
|
||||
return url.Url
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetHpcCenterUrl(ctr *Center) string {
|
||||
for _, u := range ctr.Data.HpcUrls {
|
||||
if u.Enable == "true" {
|
||||
//as.HpcCenterUrlPrefix = url.Url
|
||||
return u.Url
|
||||
func getHpcCenterUrl(cfg *config.Config, token string) string {
|
||||
centerUrl := cfg.AcBaseUrl + cfg.CenterUrl
|
||||
var cr CenterResp
|
||||
req := GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetResult(&cr).
|
||||
Get(centerUrl)
|
||||
|
||||
if err != nil || cr.Code != "0" {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, url := range cr.Data.HpcUrls {
|
||||
if url.Enable == "true" {
|
||||
return url.Url
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetEFileUrlPrefix(ctr *Center) string {
|
||||
for _, u := range ctr.Data.EfileUrls {
|
||||
if u.Enable == "true" {
|
||||
//as.EFileUrlPrefix = url.Url
|
||||
return u.Url
|
||||
func getEFileUrlPrefix(cfg *config.Config, token string) string {
|
||||
centerUrl := cfg.AcBaseUrl + cfg.CenterUrl
|
||||
var cr CenterResp
|
||||
req := GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetResult(&cr).
|
||||
Get(centerUrl)
|
||||
|
||||
if err != nil || cr.Code != "0" {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, url := range cr.Data.EfileUrls {
|
||||
if url.Enable == "true" {
|
||||
return url.Url
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetJobManagerId(cfg *config.Config, ctr *Center, token string) int {
|
||||
url := GetHpcCenterUrl(ctr) + cfg.CPConf.ClusUrl
|
||||
func getJobManagerId(cfg *config.Config, token string) int {
|
||||
url := getHpcCenterUrl(cfg, token) + cfg.CPConf.ClusUrl
|
||||
var cr ClusterResp
|
||||
req := GetRestyRequest(TIMEOUT)
|
||||
req := GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetResult(&cr).
|
||||
|
@ -253,16 +198,15 @@ func GetJobManagerId(cfg *config.Config, ctr *Center, token string) int {
|
|||
}
|
||||
|
||||
for _, datum := range cr.Data {
|
||||
//as.JobManagerId = datum.Id
|
||||
return datum.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetGroupId(baseUrlAi string, getGroupMembers string, token string) string {
|
||||
url := baseUrlAi + getGroupMembers
|
||||
func getGroupId(cfg *config.Config, token string) string {
|
||||
url := cfg.AcBaseUrl + cfg.UserConf.GetGroupMembers
|
||||
var gm GroupMembers
|
||||
req := GetRestyRequest(TIMEOUT)
|
||||
req := GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetResult(&gm).
|
||||
|
@ -274,22 +218,21 @@ func GetGroupId(baseUrlAi string, getGroupMembers string, token string) string {
|
|||
|
||||
for _, datum := range gm.Data {
|
||||
// Todo multiple groups filtering
|
||||
//as.GroupId = datum.GroupId
|
||||
return datum.GroupId
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetQueueId(cfg *config.Config, ctr *Center, token string) string {
|
||||
url := GetHpcCenterUrl(ctr) + cfg.JobConf.GetQueueNames
|
||||
func getQueueId(cfg *config.Config, token string) string {
|
||||
url := getHpcCenterUrl(cfg, token) + cfg.JobConf.GetQueueNames
|
||||
|
||||
var qn QueueNamesResp
|
||||
req := GetRestyRequest(TIMEOUT)
|
||||
req := GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("username", cfg.User).
|
||||
SetQueryString("strJobManagerID" + "=" + strconv.Itoa(GetJobManagerId(cfg, ctr, token))).
|
||||
SetQueryString("strJobManagerID" + "=" + strconv.Itoa(getJobManagerId(cfg, token))).
|
||||
SetResult(&qn).
|
||||
Get(url)
|
||||
|
||||
|
@ -297,36 +240,10 @@ func GetQueueId(cfg *config.Config, ctr *Center, token string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
if qn.Data == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, datum := range qn.Data {
|
||||
if datum.QueMaxDcuPN != "0" {
|
||||
return datum.Id
|
||||
}
|
||||
// Todo multiple groups filtering
|
||||
return datum.Id
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func IsHpcUserQueueAvail(queueId string) bool {
|
||||
return queueId != ""
|
||||
}
|
||||
|
||||
func IsAiResourceGroupAvail(ctr *Center, resourceGroup string, token string) bool {
|
||||
url := GetAiCenterUrl(ctr) + resourceGroup
|
||||
|
||||
var resourceGroupResp ResourceGroupResp
|
||||
|
||||
resourceGroupReq := httputils.GetHttpRequest()
|
||||
_, err := resourceGroupReq.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
SetHeader("token", token).
|
||||
SetResult(&resourceGroupResp).
|
||||
Get(url)
|
||||
if err != nil || resourceGroupResp.Code != "0" || resourceGroupResp.Data.Dcu == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package common
|
||||
|
||||
const (
|
||||
TIMEOUT int64 = 20
|
||||
CLUSTER_AVAIL_TMP = "11250" // 华东一区【昆山】
|
||||
SUCCESS = "0"
|
||||
)
|
||||
|
||||
// stop logic
|
||||
const (
|
||||
NOT_EXIST = "-1"
|
||||
COMPLETED_OR_FAILED = "816820"
|
||||
)
|
|
@ -200,74 +200,3 @@ type QueueNamesResp struct {
|
|||
QueueEnabled bool `json:"queueEnabled"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type Center struct {
|
||||
Code string `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
IngressUrls []interface{} `json:"ingressUrls"`
|
||||
EfileUrls []struct {
|
||||
NodeName string `json:"nodeName"`
|
||||
Enable string `json:"enable"`
|
||||
AppId string `json:"appId,omitempty"`
|
||||
FastTransEnable string `json:"fastTransEnable"`
|
||||
AppSecret string `json:"appSecret,omitempty"`
|
||||
UdpPort string `json:"udpPort"`
|
||||
IsManagerNode string `json:"isManagerNode,omitempty"`
|
||||
Version string `json:"version"`
|
||||
Url string `json:"url"`
|
||||
} `json:"efileUrls"`
|
||||
EshellUrls []struct {
|
||||
NodeName string `json:"nodeName"`
|
||||
Enable string `json:"enable"`
|
||||
AppId string `json:"appId"`
|
||||
FastTransEnable string `json:"fastTransEnable"`
|
||||
AppSecret string `json:"appSecret"`
|
||||
UdpPort string `json:"udpPort"`
|
||||
IsManagerNode string `json:"isManagerNode"`
|
||||
Version string `json:"version"`
|
||||
Url string `json:"url"`
|
||||
} `json:"eshellUrls"`
|
||||
HpcUrls []struct {
|
||||
Enable string `json:"enable"`
|
||||
IsManagerNode string `json:"isManagerNode"`
|
||||
Version string `json:"version"`
|
||||
Url string `json:"url"`
|
||||
NodeName string `json:"nodeName,omitempty"`
|
||||
AppId string `json:"appId,omitempty"`
|
||||
FastTransEnable string `json:"fastTransEnable,omitempty"`
|
||||
AppSecret string `json:"appSecret,omitempty"`
|
||||
UdpPort string `json:"udpPort,omitempty"`
|
||||
} `json:"hpcUrls"`
|
||||
AiUrls []struct {
|
||||
NodeName string `json:"nodeName"`
|
||||
Enable string `json:"enable"`
|
||||
AppId string `json:"appId"`
|
||||
FastTransEnable string `json:"fastTransEnable"`
|
||||
AppSecret string `json:"appSecret"`
|
||||
UdpPort string `json:"udpPort"`
|
||||
IsManagerNode string `json:"isManagerNode"`
|
||||
Version string `json:"version"`
|
||||
Url string `json:"url"`
|
||||
SchedulerType string `json:"schedulerType"`
|
||||
} `json:"aiUrls"`
|
||||
EshellSshHosts []struct {
|
||||
Enable string `json:"enable"`
|
||||
Url string `json:"url"`
|
||||
} `json:"eshellSshHosts"`
|
||||
InternetSshHosts []struct {
|
||||
LimitState string `json:"limitState"`
|
||||
Url string `json:"url"`
|
||||
} `json:"internetSshHosts"`
|
||||
ClusterUserInfo struct {
|
||||
UserName string `json:"userName"`
|
||||
HomePath string `json:"homePath"`
|
||||
UserId string `json:"userId"`
|
||||
UserGroup string `json:"userGroup"`
|
||||
UserShell string `json:"userShell"`
|
||||
} `json:"clusterUserInfo"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
|
|
@ -34,19 +34,17 @@ type CPConf struct {
|
|||
type AiConf struct {
|
||||
BaseUrlAi string
|
||||
GetImageListAi string
|
||||
GetInstanceAi string
|
||||
GetTaskAi string
|
||||
SubmitTaskAi string
|
||||
DeleteTaskAi string
|
||||
GetResourceSpec string
|
||||
GetInstanceLog string
|
||||
StopTaskAi string
|
||||
}
|
||||
|
||||
// 曙光作业
|
||||
type JobConf struct {
|
||||
GetQueueNames string
|
||||
GetJobList string
|
||||
GetJobDetail string
|
||||
}
|
||||
|
||||
// 曙光文件
|
||||
|
@ -54,7 +52,6 @@ type FileConf struct {
|
|||
GetFileList string
|
||||
GetFile string
|
||||
UploadFile string
|
||||
ExistFile string
|
||||
}
|
||||
|
||||
// 曙光用户资源
|
||||
|
@ -66,12 +63,5 @@ type UserConf struct {
|
|||
|
||||
// 曙光容器
|
||||
type ContainerConf struct {
|
||||
GetInstanceAi string
|
||||
GetNodeResources string
|
||||
GetInstanceServiceList string
|
||||
GetInstanceServiceDetail string
|
||||
StartInstanceService string
|
||||
StopInstanceService string
|
||||
CreateInstanceService string
|
||||
GetImageList string
|
||||
GetNodeResources string
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/pkg/utils/httputils"
|
||||
|
@ -27,16 +26,11 @@ func NewCpuCoreLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CpuCoreLo
|
|||
|
||||
func (l *CpuCoreLogic) CpuCore(in *hpcAC.CpuCoreReq) (*hpcAC.CpuCoreResp, error) {
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
url := asv.GetHpcCenterUrlPrefix() + "/hpc/openapi/v2/view/cpucore/state"
|
||||
url := common.HpcCenterUrlPrefix() + "/hpc/openapi/v2/view/cpucore/state"
|
||||
var cpuCoreResp *hpcAC.CpuCoreResp
|
||||
acHttpRequest := httputils.GetHttpRequest()
|
||||
acHttpRequest.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type CreateInstanceServiceLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewCreateInstanceServiceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateInstanceServiceLogic {
|
||||
return &CreateInstanceServiceLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateInstanceServiceLogic) CreateInstanceService(in *hpcAC.CreateInstanceServiceReq) (*hpcAC.CreateInstanceServiceResp, error) {
|
||||
resp := &hpcAC.CreateInstanceServiceResp{}
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.CreateInstanceService
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("taskGroup", PYTORCH_TASK).
|
||||
SetBody(in.Data).
|
||||
SetResult(resp).
|
||||
Post(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -3,7 +3,6 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
@ -36,14 +35,9 @@ func (l *DeleteJobLogic) DeleteJob(in *hpcAC.DeleteJobReq) (*hpcAC.DeleteJobResp
|
|||
|
||||
deleteJobUrl := "/hpc/openapi/v2/jobs?"
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
c := http.Client{Timeout: time.Duration(3) * time.Second}
|
||||
|
||||
|
@ -51,7 +45,7 @@ func (l *DeleteJobLogic) DeleteJob(in *hpcAC.DeleteJobReq) (*hpcAC.DeleteJobResp
|
|||
params.Add("jobMethod", "5")
|
||||
params.Add("strJobInfoMap", in.StrJobInfoMap)
|
||||
|
||||
reqUrl, err := http.NewRequest("DELETE", asv.GetHpcCenterUrlPrefix()+deleteJobUrl+params.Encode(), nil)
|
||||
reqUrl, err := http.NewRequest("DELETE", common.HpcCenterUrlPrefix()+deleteJobUrl+params.Encode(), nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
)
|
||||
|
||||
type DeleteTaskAiLogic struct {
|
||||
|
@ -30,28 +31,19 @@ func NewDeleteTaskAiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Dele
|
|||
|
||||
func (l *DeleteTaskAiLogic) DeleteTaskAi(in *hpcAC.DeleteTaskAiReq) (*hpcAC.DeleteTaskAiResp, error) {
|
||||
resp := &hpcAC.DeleteTaskAiResp{}
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.DeleteTaskAi
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.AiConf.DeleteTaskAi
|
||||
|
||||
formData := map[string]string{
|
||||
"ids": in.Ids,
|
||||
}
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetHeader(ContentType, ApplicationFromUrlencoded).
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetInferUrlLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetInferUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetInferUrlLogic {
|
||||
return &GetInferUrlLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetInferUrlLogic) GetInferUrl(in *hpcAC.GetInferUrlReq) (*hpcAC.GetInferUrlResp, error) {
|
||||
respInstance := &hpcAC.GetInstanceListResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.GetInstanceAi + "/task"
|
||||
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryString("instanceServiceName" + EQUAL + in.ModelName + "_" + in.Type + "_" + in.Card).
|
||||
SetResult(respInstance).
|
||||
Get(reqUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &hpcAC.GetInferUrlResp{}
|
||||
detailResp := &hpcAC.GetInstanceDetailResp{}
|
||||
var detailUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.GetInstanceAi + "/" + respInstance.Data[0].Id + "/detail"
|
||||
|
||||
//调用详情接口获取accessUrl
|
||||
reqDetail := common.GetRestyRequest(3)
|
||||
_, _ = reqDetail.SetHeader("token", token).
|
||||
SetResult(detailResp).
|
||||
Get(detailUrl)
|
||||
|
||||
resp.Code = "200"
|
||||
resp.Url = detailResp.Data.ContainerPortInfoList[0].AccessUrl
|
||||
resp.Message = "success"
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
|
@ -28,24 +29,15 @@ func NewGetInstanceListAiLogic(ctx context.Context, svcCtx *svc.ServiceContext)
|
|||
|
||||
func (l *GetInstanceListAiLogic) GetInstanceListAi(in *hpcAC.GetInstanceListReq) (*hpcAC.GetInstanceListResp, error) {
|
||||
resp := &hpcAC.GetInstanceListResp{}
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.GetInstanceAi + "/task"
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.GetInstanceAi + "/task"
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryString("start" + EQUAL + strconv.Itoa(int(in.Start))).
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
@ -33,14 +32,28 @@ func (l *GetComputingPowerLogic) GetComputingPower(in *hpcAC.ResourceReq) (*hpcA
|
|||
|
||||
cpConf := l.svcCtx.Config.CPConf
|
||||
|
||||
tokenUrl := cpConf.AcBaseUrl + cpConf.AuthUrl
|
||||
|
||||
//获取token
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
var tokenResp common.TokenResp
|
||||
var token string
|
||||
|
||||
tokenReq := httputils.GetHttpRequest()
|
||||
_, err := tokenReq.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
SetHeader("user", l.svcCtx.Config.User).
|
||||
SetHeader("password", l.svcCtx.Config.Password).
|
||||
SetHeader("orgId", l.svcCtx.Config.OrgId).
|
||||
SetResult(&tokenResp).
|
||||
Post(tokenUrl)
|
||||
|
||||
if err != nil || tokenResp.Code != "0" {
|
||||
return resp, nil
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
|
||||
for _, datum := range tokenResp.Data {
|
||||
if datum.ClusterId != "0" {
|
||||
token = datum.Token
|
||||
}
|
||||
}
|
||||
|
||||
//获取 hpc prefix url
|
||||
|
@ -49,7 +62,7 @@ func (l *GetComputingPowerLogic) GetComputingPower(in *hpcAC.ResourceReq) (*hpcA
|
|||
var hpc_prefix_url string
|
||||
|
||||
centerReq := httputils.GetHttpRequest()
|
||||
_, err := centerReq.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
_, err = centerReq.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
SetHeader("token", token).
|
||||
SetResult(¢erResp).
|
||||
Get(centerUrl)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
@ -27,33 +28,23 @@ func NewGetFileListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFi
|
|||
// 曙光文件接口
|
||||
func (l *GetFileListLogic) GetFileList(in *hpcAC.GetFileListReq) (*hpcAC.GetFileListResp, error) {
|
||||
resp := &hpcAC.GetFileListResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
var reqUrl = common.EFileUrlPrefix() + l.svcCtx.Config.FileConf.GetFileList
|
||||
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
fileUrlPrefix := asv.GetEFileUrlPrefix()
|
||||
if fileUrlPrefix == "" {
|
||||
return nil, errors.New("fileUrlPrefix is empty")
|
||||
}
|
||||
var reqUrl = fileUrlPrefix + l.svcCtx.Config.FileConf.GetFileList
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryParam("limit", strconv.Itoa(int(in.Limit))).
|
||||
SetQueryParam("path", in.Path).
|
||||
SetQueryParam("start", strconv.Itoa(int(in.Start))).
|
||||
//SetQueryString("limit" + EQUAL + strconv.Itoa(int(in.Limit))).
|
||||
//SetQueryString("order" + EQUAL + in.Order).
|
||||
//SetQueryString("orderBy" + EQUAL + in.OrderBy).
|
||||
//SetQueryString("path" + EQUAL + in.Path).
|
||||
//SetQueryString("start" + EQUAL + strconv.Itoa(int(in.Start))).
|
||||
//SetQueryString("keyWord" + EQUAL + in.KeyWord).
|
||||
SetQueryString("limit" + EQUAL + strconv.Itoa(int(in.Limit))).
|
||||
SetQueryString("order" + EQUAL + in.Order).
|
||||
SetQueryString("orderBy" + EQUAL + in.OrderBy).
|
||||
SetQueryString("path" + EQUAL + in.Path).
|
||||
SetQueryString("start" + EQUAL + strconv.Itoa(int(in.Start))).
|
||||
SetQueryString("keyWord" + EQUAL + in.KeyWord).
|
||||
SetResult(resp).
|
||||
Get(reqUrl)
|
||||
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetFileLogic struct {
|
||||
|
@ -25,22 +27,15 @@ func NewGetFileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFileLo
|
|||
|
||||
func (l *GetFileLogic) GetFile(in *hpcAC.GetFileReq) (*hpcAC.GetFileResp, error) {
|
||||
resp := &hpcAC.GetFileResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
var reqUrl = common.EFileUrlPrefix() + l.svcCtx.Config.FileConf.GetFile
|
||||
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
fileUrlPrefix := asv.GetEFileUrlPrefix()
|
||||
if fileUrlPrefix == "" {
|
||||
return nil, errors.New("fileUrlPrefix is empty")
|
||||
}
|
||||
var reqUrl = fileUrlPrefix + l.svcCtx.Config.FileConf.GetFile
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
rpse, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryParam("path", in.Path).
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
@ -36,14 +35,28 @@ func (l *GetGeneralInfoLogic) GetGeneralInfo(in *hpcAC.ResourceReq) (*hpcAC.GiRe
|
|||
cpConf := l.svcCtx.Config.CPConf
|
||||
sgConf := l.svcCtx.Config.ShuguangConf
|
||||
|
||||
tokenUrl := cpConf.AcBaseUrl + cpConf.AuthUrl
|
||||
|
||||
//获取token
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
var tokenResp common.TokenResp
|
||||
var token string
|
||||
|
||||
tokenReq := httputils.GetHttpRequest()
|
||||
_, err := tokenReq.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
SetHeader("user", l.svcCtx.Config.User).
|
||||
SetHeader("password", l.svcCtx.Config.Password).
|
||||
SetHeader("orgId", l.svcCtx.Config.OrgId).
|
||||
SetResult(&tokenResp).
|
||||
Post(tokenUrl)
|
||||
|
||||
if err != nil || tokenResp.Code != "0" {
|
||||
return resp, nil
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
|
||||
for _, datum := range tokenResp.Data {
|
||||
if datum.ClusterId != "0" {
|
||||
token = datum.Token
|
||||
}
|
||||
}
|
||||
|
||||
//获取 ai, hpc prefix url
|
||||
|
@ -53,7 +66,7 @@ func (l *GetGeneralInfoLogic) GetGeneralInfo(in *hpcAC.ResourceReq) (*hpcAC.GiRe
|
|||
var ai_prefix_url string
|
||||
|
||||
centerReq := httputils.GetHttpRequest()
|
||||
_, err := centerReq.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
_, err = centerReq.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
SetHeader("token", token).
|
||||
SetResult(¢erResp).
|
||||
Get(centerUrl)
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetImageListAiLogic struct {
|
||||
|
@ -32,26 +34,19 @@ func NewGetImageListAiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ge
|
|||
|
||||
func (l *GetImageListAiLogic) GetImageListAi(in *hpcAC.GetImageListAiReq) (*hpcAC.GetImageListAiResp, error) {
|
||||
resp := &hpcAC.GetImageListAiResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.GetImageListAi
|
||||
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.AiConf.GetImageListAi
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryParam("acceleratorType", in.AcceleratorType).
|
||||
SetQueryParam("taskType", in.TaskType).
|
||||
SetQueryString("acceleratorType" + EQUAL + in.AcceleratorType).
|
||||
SetQueryString("taskType" + EQUAL + in.TaskType).
|
||||
SetResult(resp).
|
||||
Get(reqUrl)
|
||||
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"net/http"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetImageListLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetImageListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetImageListLogic {
|
||||
return &GetImageListLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetImageListLogic) GetImageList(in *hpcAC.GetImageListReq) (*hpcAC.GetImageListResp, error) {
|
||||
resp := &hpcAC.GetImageListResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.GetImageList
|
||||
|
||||
s := struct {
|
||||
AcceleratorType string `json:"acceleratorType"`
|
||||
ImageType string `json:"imageType"`
|
||||
}{
|
||||
AcceleratorType: in.AcceleratorType,
|
||||
ImageType: in.ImageType,
|
||||
}
|
||||
|
||||
b, _ := json.Marshal(s)
|
||||
byt := bytes.NewBuffer(b)
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
r, _ := http.NewRequest("", reqUrl, byt)
|
||||
req.RawRequest = r
|
||||
req.URL = reqUrl
|
||||
|
||||
_, err := req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("token", token).
|
||||
SetBody(byt).
|
||||
SetResult(resp).
|
||||
Send()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
|
@ -28,23 +29,15 @@ func NewGetInstanceLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ge
|
|||
|
||||
func (l *GetInstanceLogLogic) GetInstanceLog(in *hpcAC.GetInstanceLogReq) (*hpcAC.GetInstanceLogResp, error) {
|
||||
resp := &hpcAC.GetInstanceLogResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.GetInstanceLog
|
||||
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.AiConf.GetInstanceLog
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("instanceNum", in.InstanceNum).
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetInstanceServiceDetailLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetInstanceServiceDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetInstanceServiceDetailLogic {
|
||||
return &GetInstanceServiceDetailLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetInstanceServiceDetailLogic) GetInstanceServiceDetail(in *hpcAC.GetInstanceServiceDetailReq) (*hpcAC.GetInstanceServiceDetailResp, error) {
|
||||
resp := &hpcAC.GetInstanceServiceDetailResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.GetInstanceServiceDetail
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("id", in.Id).
|
||||
SetResult(resp).
|
||||
Get(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.Code != "0" {
|
||||
return nil, errors.New("get instance failed")
|
||||
}
|
||||
|
||||
if resp.Data == nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if len(resp.Data.ContainerPortInfoList) != 0 {
|
||||
for _, info := range resp.Data.ContainerPortInfoList {
|
||||
//info.CreateTime = ""
|
||||
info.ContainerServicesUrl = ""
|
||||
//info.Id = ""
|
||||
//info.ContentPath = ""
|
||||
//info.ProtocolType = ""
|
||||
//info.ContainerPort = 0
|
||||
//info.AccessUrl = ""
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type GetInstanceServiceListLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetInstanceServiceListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetInstanceServiceListLogic {
|
||||
return &GetInstanceServiceListLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetInstanceServiceListLogic) GetInstanceServiceList(in *hpcAC.GetInstanceServiceListReq) (*hpcAC.GetInstanceServiceListResp, error) {
|
||||
resp := &hpcAC.GetInstanceServiceListResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.GetInstanceServiceList
|
||||
|
||||
b, _ := json.Marshal(in.Param)
|
||||
byt := bytes.NewBuffer(b)
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
r, _ := http.NewRequest("", reqUrl, byt)
|
||||
req.RawRequest = r
|
||||
req.URL = reqUrl
|
||||
|
||||
_, err := req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("token", token).
|
||||
SetBody(byt).
|
||||
SetResult(resp).
|
||||
Send()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
|
@ -2,11 +2,10 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/pkg/utils/httputils"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
)
|
||||
|
||||
|
@ -26,31 +25,17 @@ func NewGetJobDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetJ
|
|||
|
||||
// GetJobDetail get job detail
|
||||
func (l *GetJobDetailLogic) GetJobDetail(in *hpcAC.JobDetailReq) (*hpcAC.GetJobDetailResp, error) {
|
||||
resp := &hpcAC.GetJobDetailResp{}
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
reqUrl := asv.GetHpcCenterUrlPrefix() + l.svcCtx.Config.JobConf.GetJobDetail
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
rp, err := req.
|
||||
var resp *hpcAC.GetJobDetailResp
|
||||
acHttpRequest := httputils.GetHttpRequest()
|
||||
acHttpRequest.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
SetResult(&resp).
|
||||
SetHeader("token", token).
|
||||
SetPathParam("jobId", in.JobId).
|
||||
SetResult(resp).
|
||||
Get(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println(rp)
|
||||
Get(common.HpcCenterUrlPrefix() + "/hpc/openapi/v2/jobs/" + in.JobId)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetMemberJobsLogic struct {
|
||||
|
@ -25,32 +27,19 @@ func NewGetMemberJobsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get
|
|||
|
||||
func (l *GetMemberJobsLogic) GetMemberJobs(in *hpcAC.GetMemberJobsReq) (*hpcAC.GetMemberJobsResp, error) {
|
||||
resp := &hpcAC.GetMemberJobsResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
var reqUrl = l.svcCtx.Config.AcBaseUrl + l.svcCtx.Config.UserConf.GetMemberJobs
|
||||
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
var reqUrl = l.svcCtx.Config.AcBaseUrl + l.svcCtx.Config.UserConf.GetMemberJobs
|
||||
|
||||
clusterId := asv.GetClusterId()
|
||||
if clusterId == "" {
|
||||
return nil, errors.New("clusterId is empty")
|
||||
}
|
||||
|
||||
groupId := asv.GetGroupId()
|
||||
if groupId == "" {
|
||||
return nil, errors.New("groupId is empty")
|
||||
}
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("clusterId", clusterId).
|
||||
SetPathParam("groupId", groupId).
|
||||
SetPathParam("clusterId", common.GetClusterId()).
|
||||
SetPathParam("groupId", common.GetGroupId()).
|
||||
SetPathParam("clusterUserName", l.svcCtx.Config.User).
|
||||
SetResult(resp).
|
||||
Get(reqUrl)
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetNodeResourcesLogic struct {
|
||||
|
@ -26,28 +28,16 @@ func NewGetNodeResourcesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *
|
|||
// 获取节点资源限额
|
||||
func (l *GetNodeResourcesLogic) GetNodeResources(in *hpcAC.GetNodeResourcesReq) (*hpcAC.GetNodeResourcesResp, error) {
|
||||
resp := &hpcAC.GetNodeResourcesResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.ContainerConf.GetNodeResources
|
||||
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.GetNodeResources
|
||||
|
||||
queueId := asv.GetQueueId()
|
||||
if queueId == "" {
|
||||
return nil, errors.New("queueId is empty")
|
||||
}
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
queueId := common.GetQueueId()
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryString("resourceGroup" + "=" + queueId).
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetPytorchTaskLogic struct {
|
||||
|
@ -29,24 +31,15 @@ func NewGetPytorchTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ge
|
|||
|
||||
func (l *GetPytorchTaskLogic) GetPytorchTask(in *hpcAC.GetPytorchTaskReq) (*hpcAC.GetPytorchTaskResp, error) {
|
||||
resp := &hpcAC.GetPytorchTaskResp{}
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.GetTaskAi
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.AiConf.GetTaskAi
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("taskGroup", PYTORCH_TASK).
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetResourceSpecLogic struct {
|
||||
|
@ -25,24 +27,15 @@ func NewGetResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) *G
|
|||
|
||||
func (l *GetResourceSpecLogic) GetResourceSpec(in *hpcAC.GetResourceSpecReq) (*hpcAC.GetResourceSpecResp, error) {
|
||||
resp := &hpcAC.GetResourceSpecResp{}
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.GetResourceSpec
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.AiConf.GetResourceSpec
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryString("acceleratorType" + EQUAL + in.AcceleratorType).
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetTensorflowTaskLogic struct {
|
||||
|
@ -29,24 +31,15 @@ func NewGetTensorflowTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext)
|
|||
|
||||
func (l *GetTensorflowTaskLogic) GetTensorflowTask(in *hpcAC.GetTensorflowTaskReq) (*hpcAC.GetTensorflowTaskResp, error) {
|
||||
resp := &hpcAC.GetTensorflowTaskResp{}
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.GetTaskAi
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.AiConf.GetTaskAi
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("taskGroup", TENSORFLOW_TASK).
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetUserInfoLogic struct {
|
||||
|
@ -28,16 +30,13 @@ func (l *GetUserInfoLogic) GetUserInfo(in *hpcAC.GetUserInfoReq) (*hpcAC.GetUser
|
|||
resp := &hpcAC.GetUserInfoResp{}
|
||||
var reqUrl = l.svcCtx.Config.AcBaseUrl + l.svcCtx.Config.UserConf.GetUserInfo
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetResult(resp).
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
|
@ -27,20 +26,15 @@ func NewHistoryJobDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *
|
|||
|
||||
func (l *HistoryJobDetailLogic) HistoryJobDetail(in *hpcAC.HistoryJobDetailReq) (*hpcAC.HistoryJobDetailResp, error) {
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
var url string
|
||||
if in.AcctTime != "" {
|
||||
url = fmt.Sprintf(asv.GetHpcCenterUrlPrefix()+"/hpc/openapi/v2/historyjobs/%s/%s?acctTime=%s", in.JobmanagerId, in.JobId, in.AcctTime)
|
||||
url = fmt.Sprintf(common.HpcCenterUrlPrefix()+"/hpc/openapi/v2/historyjobs/%s/%s?acctTime=%s", in.JobmanagerId, in.JobId, in.AcctTime)
|
||||
} else {
|
||||
url = fmt.Sprintf(asv.GetHpcCenterUrlPrefix()+"/hpc/openapi/v2/historyjobs/%s/%s", in.JobmanagerId, in.JobId)
|
||||
url = fmt.Sprintf(common.HpcCenterUrlPrefix()+"/hpc/openapi/v2/historyjobs/%s/%s", in.JobmanagerId, in.JobId)
|
||||
}
|
||||
|
||||
var historyJobDetail *hpcAC.HistoryJobDetailResp
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type IsExistFileLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewIsExistFileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *IsExistFileLogic {
|
||||
return &IsExistFileLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *IsExistFileLogic) IsExistFile(in *hpcAC.IsExistFileReq) (*hpcAC.IsExistFileResp, error) {
|
||||
resp := &hpcAC.IsExistFileResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
fileUrlPrefix := asv.GetEFileUrlPrefix()
|
||||
if fileUrlPrefix == "" {
|
||||
return nil, errors.New("fileUrlPrefix is empty")
|
||||
}
|
||||
var reqUrl = fileUrlPrefix + l.svcCtx.Config.FileConf.ExistFile
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("path", in.Path)
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
||||
SetBody(strings.NewReader(params.Encode())).
|
||||
SetResult(resp).
|
||||
Post(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
@ -26,16 +25,11 @@ func NewJobsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *JobsLogic {
|
|||
|
||||
func (l *JobsLogic) Jobs(in *hpcAC.JobsReq) (*hpcAC.JobsResp, error) {
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
url := asv.GetHpcCenterUrlPrefix() + "/hpc/openapi/v2/view/jobs/state?userName=" + in.UserName
|
||||
url := common.HpcCenterUrlPrefix() + "/hpc/openapi/v2/view/jobs/state?userName=" + in.UserName
|
||||
var jobsResp *hpcAC.JobsResp
|
||||
acHttpRequest := httputils.GetHttpRequest()
|
||||
acHttpRequest.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
|
|
|
@ -2,13 +2,17 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/pkg/utils/httputils"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ListHistoryJobLogic struct {
|
||||
|
@ -28,45 +32,99 @@ func NewListHistoryJobLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Li
|
|||
// ListHistoryJob list all history jobs
|
||||
func (l *ListHistoryJobLogic) ListHistoryJob(in *hpcAC.ListHistoryJobReq) (*hpcAC.ListHistoryJobResp, error) {
|
||||
|
||||
resp := &hpcAC.ListHistoryJobResp{}
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
hpcCenterUrlPrefix := asv.GetHpcCenterUrlPrefix()
|
||||
if hpcCenterUrlPrefix == "" {
|
||||
return nil, errors.New("hpcCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
jobManagerId := asv.GetJobManagerId()
|
||||
if jobManagerId == 0 {
|
||||
return nil, errors.New("jobManagerId is empty")
|
||||
}
|
||||
var resp hpcAC.ListHistoryJobResp
|
||||
historyJobUrl := "/hpc/openapi/v2/historyjobs?"
|
||||
|
||||
reqUrl := hpcCenterUrlPrefix + historyJobUrl
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
_, err := req.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
SetHeader("token", token).
|
||||
SetQueryString("strClusterNameList=" + strconv.FormatInt(int64(jobManagerId), 10)).
|
||||
SetQueryString("startTime=" + in.StartTime).
|
||||
SetQueryString("endTime=" + in.EndTime).
|
||||
SetQueryString("timeType=" + in.TimeType).
|
||||
SetQueryString("start=" + strconv.FormatInt(int64(in.Start), 10)).
|
||||
SetQueryString("limit=" + strconv.FormatInt(int64(in.Limit), 10)).
|
||||
SetQueryString("isQueryByQueueTime=" + in.IsQueryByQueueTime).
|
||||
SetResult(resp).
|
||||
Get(reqUrl)
|
||||
getACClusterIdLogic := NewGetACClusterIdLogic(l.ctx, l.svcCtx)
|
||||
clusterIdResp, _ := getACClusterIdLogic.GetACClusterId(&hpcAC.ACClusterReq{Token: token})
|
||||
clusterId := clusterIdResp.GetData().Id
|
||||
|
||||
c := http.Client{Timeout: time.Duration(60) * time.Second}
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("strClusterNameList", strconv.FormatInt(clusterId, 10))
|
||||
params.Add("startTime", in.StartTime)
|
||||
params.Add("endTime", in.EndTime)
|
||||
params.Add("timeType", in.TimeType)
|
||||
params.Add("start", strconv.FormatInt(int64(in.Start), 10))
|
||||
params.Add("limit", strconv.FormatInt(int64(in.Limit), 10))
|
||||
params.Add("isQueryByQueueTime", in.IsQueryByQueueTime)
|
||||
|
||||
reqUrl, err := http.NewRequest("GET", common.HpcCenterUrlPrefix()+historyJobUrl+params.Encode(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
reqUrl.Header.Add("token", token)
|
||||
respUrl, err := c.Do(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(respUrl.Body)
|
||||
|
||||
jsonResult, err := simplejson.NewJson(body)
|
||||
jsonData := jsonResult.Get("data")
|
||||
if jsonData.Get("total").MustInt() == 0 {
|
||||
resp.Code = "200"
|
||||
resp.Msg = "success"
|
||||
return &resp, nil
|
||||
}
|
||||
historyJobList := jsonResult.Get("data").Get("list")
|
||||
rows, err := historyJobList.Array()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(respUrl.Body)
|
||||
|
||||
var Jobs []*hpcAC.HistoryJobList
|
||||
|
||||
for index := range rows {
|
||||
jobShuguang := historyJobList.GetIndex(index)
|
||||
var job hpcAC.HistoryJobList
|
||||
|
||||
job.AppType = jobShuguang.Get("appType").MustString()
|
||||
job.JobId = jobShuguang.Get("jobId").MustString()
|
||||
job.JobName = jobShuguang.Get("jobName").MustString()
|
||||
job.JobStartTime = jobShuguang.Get("jobStartTime").MustString()
|
||||
job.Queue = jobShuguang.Get("queue").MustString()
|
||||
job.JobState = jobShuguang.Get("jobState").MustString()
|
||||
job.JobEndTime = jobShuguang.Get("jobEndTime").MustString()
|
||||
job.JobExecHost = jobShuguang.Get("jobExecHost").MustString()
|
||||
job.JobWalltimeUsed = jobShuguang.Get("jobWalltimeUsed").MustString()
|
||||
job.UserName = jobShuguang.Get("userName").MustString()
|
||||
job.JobExitStatus = int32(jobShuguang.Get("jobExitStatus").MustInt())
|
||||
job.AcctTime = jobShuguang.Get("acctTime").MustString()
|
||||
job.JobProcNum = int32(jobShuguang.Get("jobProcNum").MustInt())
|
||||
job.Nodect = int32(jobShuguang.Get("nodect").MustInt())
|
||||
job.Workdir = jobShuguang.Get("workdir").MustString()
|
||||
|
||||
startTime, err := time.Parse(l.svcCtx.Config.ShuguangConf.Layout, jobShuguang.Get("jobStartTime").MustString())
|
||||
if err == nil {
|
||||
job.JobStartTime = startTime.String()
|
||||
}
|
||||
|
||||
Jobs = append(Jobs, &job)
|
||||
|
||||
}
|
||||
|
||||
if jsonResult.Get("data").Get("code").MustInt() == 0 {
|
||||
resp.Code = "200"
|
||||
}
|
||||
resp.Msg = jsonResult.Get("data").Get("msg").MustString()
|
||||
data := hpcAC.HistoryJobData{}
|
||||
data.List = Jobs
|
||||
resp.Data = &data
|
||||
|
||||
return &resp, nil
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/bitly/go-simplejson"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
@ -37,24 +36,23 @@ func (l *ListJobLogic) ListJob(in *hpcAC.ListJobReq) (*hpcAC.ListJobResp, error)
|
|||
var resp hpcAC.ListJobResp
|
||||
jobUrl := "/hpc/openapi/v2/jobs?"
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
getACClusterIdLogic := NewGetACClusterIdLogic(l.ctx, l.svcCtx)
|
||||
clusterIdResp, _ := getACClusterIdLogic.GetACClusterId(&hpcAC.ACClusterReq{Token: token})
|
||||
clusterId := clusterIdResp.GetData().Id
|
||||
|
||||
c := http.Client{Timeout: time.Duration(3) * time.Second}
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("strClusterIDList", strconv.FormatInt(int64(asv.GetJobManagerId()), 10))
|
||||
params.Add("strClusterIDList", strconv.FormatInt(clusterId, 10))
|
||||
|
||||
reqUrl, err := http.NewRequest("GET", asv.GetHpcCenterUrlPrefix()+jobUrl+params.Encode(), nil)
|
||||
reqUrl, err := http.NewRequest("GET", common.HpcCenterUrlPrefix()+jobUrl+params.Encode(), nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
reqUrl.Header.Add("token", token)
|
||||
|
@ -62,7 +60,7 @@ func (l *ListJobLogic) ListJob(in *hpcAC.ListJobReq) (*hpcAC.ListJobResp, error)
|
|||
respUrl, err := c.Do(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(respUrl.Body)
|
||||
|
|
|
@ -3,7 +3,6 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
@ -35,18 +34,13 @@ func (l *ListJobManagerLogic) ListJobManager(in *hpcAC.JobManagerReq) (*hpcAC.Li
|
|||
var resp hpcAC.ListJobManagerResp
|
||||
jobManagerUrl := "/hpc/openapi/v2/cluster"
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
c := http.Client{Timeout: time.Duration(3) * time.Second}
|
||||
|
||||
reqUrl, err := http.NewRequest("GET", asv.GetHpcCenterUrlPrefix()+jobManagerUrl, nil)
|
||||
reqUrl, err := http.NewRequest("GET", common.HpcCenterUrlPrefix()+jobManagerUrl, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
@ -27,21 +26,9 @@ func NewParaStorQuotaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Par
|
|||
// ParaStor Quota
|
||||
func (l *ParaStorQuotaLogic) ParaStorQuota(in *hpcAC.ParaStorQuotaReq) (*hpcAC.ParaStorQuotaResp, error) {
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
token := common.GetToken()
|
||||
|
||||
hpcCenterUrlPrefix := asv.GetHpcCenterUrlPrefix()
|
||||
if hpcCenterUrlPrefix == "" {
|
||||
return nil, errors.New("hpcCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
url := hpcCenterUrlPrefix + "/hpc/openapi/v2/parastor/quota/usernames/" + l.svcCtx.Config.User
|
||||
url := common.HpcCenterUrlPrefix() + "/hpc/openapi/v2/parastor/quota/usernames/" + l.svcCtx.Config.User
|
||||
var acResp *hpcAC.ParaStorQuotaResp
|
||||
acHttpRequest := httputils.GetHttpRequest()
|
||||
_, err := acHttpRequest.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
|
@ -29,17 +28,8 @@ func NewQueryQueueDetailsLogic(ctx context.Context, svcCtx *svc.ServiceContext)
|
|||
func (l *QueryQueueDetailsLogic) QueryQueueDetails(in *hpcAC.QueueReq) (*hpcAC.QueueDetailsResp, error) {
|
||||
resp := hpcAC.QueueDetailsResp{}
|
||||
url := fmt.Sprintf("/hpc/openapi/v2/userquotas/queues?strJobManagerID=%s", in.StrJobManagerID)
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
_, _ = utils.Get(token, asv.GetHpcCenterUrlPrefix(), url, nil, nil, &resp)
|
||||
token := common.GetToken()
|
||||
_, _ = utils.Get(token, common.HpcCenterUrlPrefix(), url, nil, nil, &resp)
|
||||
|
||||
return &resp, nil
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
@ -27,27 +26,12 @@ func NewQueryUserQuotasLimitLogic(ctx context.Context, svcCtx *svc.ServiceContex
|
|||
|
||||
// QueryUserQuotasLimit 查询用户资源限制信息
|
||||
func (l *QueryUserQuotasLimitLogic) QueryUserQuotasLimit(in *hpcAC.QueueReq) (*hpcAC.UserQuotasLimitResp, error) {
|
||||
|
||||
resp := &hpcAC.UserQuotasLimitResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
token := common.GetToken()
|
||||
jobManagerId := common.GetJobManagerId()
|
||||
|
||||
jobManagerId := asv.GetJobManagerId()
|
||||
if jobManagerId == 0 {
|
||||
return nil, errors.New("jobManagerId is empty")
|
||||
}
|
||||
|
||||
hpcCenterUrlPrefix := asv.GetHpcCenterUrlPrefix()
|
||||
if hpcCenterUrlPrefix == "" {
|
||||
return nil, errors.New("hpcCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
quotaUrl := hpcCenterUrlPrefix + l.svcCtx.Config.UserLimitUrl
|
||||
quotaUrl := common.HpcCenterUrlPrefix() + l.svcCtx.Config.UserLimitUrl
|
||||
|
||||
quotaReq := httputils.GetHttpRequest()
|
||||
_, err := quotaReq.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
@ -27,16 +26,11 @@ func NewQueueJobsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueueJo
|
|||
// QueueJobs
|
||||
func (l *QueueJobsLogic) QueueJobs(in *hpcAC.QueueJobsReq) (*hpcAC.QueueJobsResp, error) {
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
url := asv.GetHpcCenterUrlPrefix() + "/hpc/openapi/v2/view/queue/jobs?userName=" + in.UserName
|
||||
url := common.HpcCenterUrlPrefix() + "/hpc/openapi/v2/view/queue/jobs?userName=" + in.UserName
|
||||
var queueJobsResp *hpcAC.QueueJobsResp
|
||||
acHttpRequest := httputils.GetHttpRequest()
|
||||
acHttpRequest.SetHeader(httputils.ContentType, httputils.ApplicationJson).
|
||||
|
|
|
@ -2,12 +2,11 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/pkg/utils"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type SelectQueueByUserLogic struct {
|
||||
|
@ -27,39 +26,9 @@ func NewSelectQueueByUserLogic(ctx context.Context, svcCtx *svc.ServiceContext)
|
|||
// SelectQueueByUser 查询用户可访问队列列表
|
||||
func (l *SelectQueueByUserLogic) SelectQueueByUser(in *hpcAC.QueueReq) (*hpcAC.QueueResp, error) {
|
||||
|
||||
resp := &hpcAC.QueueResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
resp := hpcAC.QueueResp{}
|
||||
url := fmt.Sprintf("/hpc/openapi/v2/queuenames/users/%s/?strJobManagerID=%s", in.User, in.StrJobManagerID)
|
||||
_, _ = utils.Get("", l.svcCtx.Config.ClusterUrl, url, nil, nil, &resp)
|
||||
|
||||
hpcCenterUrlPrefix := asv.GetHpcCenterUrlPrefix()
|
||||
if hpcCenterUrlPrefix == "" {
|
||||
return nil, errors.New("hpcCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
jobManagerId := asv.GetJobManagerId()
|
||||
if jobManagerId == 0 {
|
||||
return nil, errors.New("jobManagerId is empty")
|
||||
}
|
||||
|
||||
reqUrl := hpcCenterUrlPrefix + l.svcCtx.Config.JobConf.GetQueueNames
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryParam("strJobManagerID", strconv.Itoa(int(jobManagerId))).
|
||||
SetPathParam("username", l.svcCtx.Config.User).
|
||||
SetResult(resp).
|
||||
Get(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
return &resp, nil
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type StartInstanceServiceLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewStartInstanceServiceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StartInstanceServiceLogic {
|
||||
return &StartInstanceServiceLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *StartInstanceServiceLogic) StartInstanceService(in *hpcAC.StartInstanceServiceReq) (*hpcAC.StartInstanceServiceResp, error) {
|
||||
resp := &hpcAC.StartInstanceServiceResp{}
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.StartInstanceService
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryString("instanceServiceId" + EQUAL + in.InstanceServiceId).
|
||||
SetResult(resp).
|
||||
Post(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type StopInstanceServiceLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewStopInstanceServiceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StopInstanceServiceLogic {
|
||||
return &StopInstanceServiceLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *StopInstanceServiceLogic) StopInstanceService(in *hpcAC.StopInstanceServiceReq) (*hpcAC.StopInstanceServiceResp, error) {
|
||||
resp := &hpcAC.StopInstanceServiceResp{}
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
aiCenterUrlPrefix := asv.GetAiCenterUrlPrefix()
|
||||
if aiCenterUrlPrefix == "" {
|
||||
return nil, errors.New("aiCenterUrlPrefix is empty")
|
||||
}
|
||||
|
||||
var reqUrl = aiCenterUrlPrefix + l.svcCtx.Config.ContainerConf.StopInstanceService
|
||||
|
||||
qmap := make(map[string]string)
|
||||
if len(in.Ids) == 0 {
|
||||
return nil, errors.New("ids is empty")
|
||||
}
|
||||
|
||||
for _, id := range in.Ids {
|
||||
qmap["ids"] = id
|
||||
}
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetQueryParams(qmap).
|
||||
SetResult(resp).
|
||||
Post(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type StopTaskAiLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewStopTaskAiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StopTaskAiLogic {
|
||||
return &StopTaskAiLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *StopTaskAiLogic) StopTaskAi(in *hpcAC.StopTaskAiReq) (*hpcAC.StopTaskAiResp, error) {
|
||||
resp := &hpcAC.StopTaskAiResp{}
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
var reqUrl = asv.GetAiCenterUrlPrefix() + l.svcCtx.Config.StopTaskAi
|
||||
|
||||
respTmp := struct {
|
||||
Code string `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data interface{} `json:"data"`
|
||||
}{}
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("taskId", in.Id).
|
||||
SetResult(&respTmp).
|
||||
Post(reqUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &hpcAC.StopTaskAiData{}
|
||||
resp.Code = respTmp.Code
|
||||
resp.Msg = respTmp.Msg
|
||||
resp.Data = d
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type SubmitImageInferTaskLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewSubmitImageInferTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SubmitImageInferTaskLogic {
|
||||
return &SubmitImageInferTaskLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SubmitImageInferTaskLogic) SubmitImageInferTask(in *hpcAC.SubmitImageInferTaskReq) (*hpcAC.SubmitImageInferTaskResp, error) {
|
||||
resp := &hpcAC.SubmitImageInferTaskResp{}
|
||||
detailResp := &hpcAC.GetInstanceDetailResp{}
|
||||
var detailUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.GetInstanceAi + "/" + in.Id + "/detail"
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
//调用详情接口获取accessUrl
|
||||
reqDetail := common.GetRestyRequest(3)
|
||||
_, err := reqDetail.SetHeader("token", token).
|
||||
SetResult(detailResp).
|
||||
Get(detailUrl)
|
||||
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err = req.
|
||||
SetFileReader("file", "train.py", bytes.NewReader([]byte(in.File))).
|
||||
SetFormData(map[string]string{
|
||||
"path": in.Path,
|
||||
"cover": in.Cover,
|
||||
}).
|
||||
SetResult(resp).
|
||||
Post(detailResp.Data.ContainerPortInfoList[0].AccessUrl + "/image")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
@ -34,21 +33,16 @@ func NewSubmitJobLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SubmitJ
|
|||
func (l *SubmitJobLogic) SubmitJob(in *hpcAC.SubmitJobReq) (*hpcAC.SubmitJobResp, error) {
|
||||
resp := &hpcAC.SubmitJobResp{}
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
jobSubmitUrl := "/hpc/openapi/v2/apptemplates/{apptype}/{appname}/job"
|
||||
jobSubmitUrl = strings.Replace(jobSubmitUrl, "{apptype}", in.Apptype, -1)
|
||||
jobSubmitUrl = strings.Replace(jobSubmitUrl, "{appname}", in.Appname, -1)
|
||||
|
||||
jsonStr, _ := json.Marshal(in)
|
||||
req_url, err := http.NewRequest("POST", asv.GetHpcCenterUrlPrefix()+jobSubmitUrl, bytes.NewBuffer(jsonStr))
|
||||
req_url, err := http.NewRequest("POST", common.HpcCenterUrlPrefix()+jobSubmitUrl, bytes.NewBuffer(jsonStr))
|
||||
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
req_url.Header.Add("content-type", "application/json")
|
||||
req_url.Header.Add("token", token)
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type SubmitPytorchTaskLogic struct {
|
||||
|
@ -26,19 +28,15 @@ func NewSubmitPytorchTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext)
|
|||
// 曙光智算接口
|
||||
func (l *SubmitPytorchTaskLogic) SubmitPytorchTask(in *hpcAC.SubmitPytorchTaskReq) (*hpcAC.SubmitTaskAiResp, error) {
|
||||
resp := &hpcAC.SubmitTaskAiResp{}
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.SubmitTaskAi
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
var reqUrl = asv.GetAiCenterUrlPrefix() + l.svcCtx.Config.AiConf.SubmitTaskAi
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(20)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("taskGroup", PYTORCH_TASK).
|
||||
|
|
|
@ -3,10 +3,12 @@ package logic
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
"log"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type SubmitTensorflowTaskLogic struct {
|
||||
|
@ -25,19 +27,15 @@ func NewSubmitTensorflowTaskLogic(ctx context.Context, svcCtx *svc.ServiceContex
|
|||
|
||||
func (l *SubmitTensorflowTaskLogic) SubmitTensorflowTask(in *hpcAC.SubmitTensorflowTaskReq) (*hpcAC.SubmitTaskAiResp, error) {
|
||||
resp := &hpcAC.SubmitTaskAiResp{}
|
||||
var reqUrl = common.AiCenterUrlPrefix() + l.svcCtx.Config.AiConf.SubmitTaskAi
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
|
||||
var reqUrl = asv.GetAiCenterUrlPrefix() + l.svcCtx.Config.AiConf.SubmitTaskAi
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(20)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetPathParam("taskGroup", TENSORFLOW_TASK).
|
||||
|
|
|
@ -5,8 +5,10 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"log"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
|
@ -28,18 +30,15 @@ func NewUploadFileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Upload
|
|||
|
||||
func (l *UploadFileLogic) UploadFile(in *hpcAC.UploadFileReq) (*hpcAC.UploadFileResp, error) {
|
||||
resp := &hpcAC.UploadFileResp{}
|
||||
var reqUrl = common.EFileUrlPrefix() + l.svcCtx.Config.FileConf.UploadFile
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
token := common.GetToken()
|
||||
if token == "" {
|
||||
log.Println("获取token失败")
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
var reqUrl = asv.GetEFileUrlPrefix() + l.svcCtx.Config.FileConf.UploadFile
|
||||
|
||||
req := common.GetRestyRequest(common.TIMEOUT)
|
||||
req := common.GetRestyRequest(3)
|
||||
_, err := req.
|
||||
SetHeader("token", token).
|
||||
SetFileReader("file", "train.py", bytes.NewReader([]byte(in.File))).
|
||||
|
|
|
@ -2,7 +2,6 @@ package logic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/hpcAC"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
|
@ -26,16 +25,11 @@ func NewWallTimeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WallTime
|
|||
|
||||
func (l *WallTimeLogic) WallTime(in *hpcAC.WallTimeReq) (*hpcAC.WallTimeResp, error) {
|
||||
|
||||
asv, found := l.svcCtx.AuthServiceMap[common.CLUSTER_AVAIL_TMP]
|
||||
if !found {
|
||||
return nil, errors.New("可用区域不存在")
|
||||
}
|
||||
token := asv.GetToken()
|
||||
if token == "" {
|
||||
return nil, errors.New("获取token失败")
|
||||
}
|
||||
getTokenLogic := NewGetACTokenLogic(l.ctx, l.svcCtx)
|
||||
tokenResp, _ := getTokenLogic.GetACToken(&hpcAC.ACTokenReq{})
|
||||
token := tokenResp.GetData().Token
|
||||
|
||||
url := asv.GetHpcCenterUrlPrefix() + "/hpc/openapi/v2/view/walltime/users/" + in.Username
|
||||
url := common.HpcCenterUrlPrefix() + "/hpc/openapi/v2/view/walltime/users/" + in.Username
|
||||
var wallTimeResp *hpcAC.WallTimeResp
|
||||
acHttpRequest := httputils.GetHttpRequest()
|
||||
acHttpRequest.
|
||||
|
|
|
@ -175,11 +175,6 @@ func (s *HpcACServer) DeleteTaskAi(ctx context.Context, in *hpcAC.DeleteTaskAiRe
|
|||
return l.DeleteTaskAi(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) StopTaskAi(ctx context.Context, in *hpcAC.StopTaskAiReq) (*hpcAC.StopTaskAiResp, error) {
|
||||
l := logic.NewStopTaskAiLogic(ctx, s.svcCtx)
|
||||
return l.StopTaskAi(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) GetResourceSpec(ctx context.Context, in *hpcAC.GetResourceSpecReq) (*hpcAC.GetResourceSpecResp, error) {
|
||||
l := logic.NewGetResourceSpecLogic(ctx, s.svcCtx)
|
||||
return l.GetResourceSpec(in)
|
||||
|
@ -195,9 +190,9 @@ func (s *HpcACServer) GetInstanceListAi(ctx context.Context, in *hpcAC.GetInstan
|
|||
return l.GetInstanceListAi(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) GetInferUrl(ctx context.Context, in *hpcAC.GetInferUrlReq) (*hpcAC.GetInferUrlResp, error) {
|
||||
l := logic.NewGetInferUrlLogic(ctx, s.svcCtx)
|
||||
return l.GetInferUrl(in)
|
||||
func (s *HpcACServer) SubmitImageInferTask(ctx context.Context, in *hpcAC.SubmitImageInferTaskReq) (*hpcAC.SubmitImageInferTaskResp, error) {
|
||||
l := logic.NewSubmitImageInferTaskLogic(ctx, s.svcCtx)
|
||||
return l.SubmitImageInferTask(in)
|
||||
}
|
||||
|
||||
// 曙光文件接口
|
||||
|
@ -216,11 +211,6 @@ func (s *HpcACServer) UploadFile(ctx context.Context, in *hpcAC.UploadFileReq) (
|
|||
return l.UploadFile(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) IsExistFile(ctx context.Context, in *hpcAC.IsExistFileReq) (*hpcAC.IsExistFileResp, error) {
|
||||
l := logic.NewIsExistFileLogic(ctx, s.svcCtx)
|
||||
return l.IsExistFile(in)
|
||||
}
|
||||
|
||||
// 用户资源
|
||||
func (s *HpcACServer) GetUserInfo(ctx context.Context, in *hpcAC.GetUserInfoReq) (*hpcAC.GetUserInfoResp, error) {
|
||||
l := logic.NewGetUserInfoLogic(ctx, s.svcCtx)
|
||||
|
@ -232,38 +222,8 @@ func (s *HpcACServer) GetMemberJobs(ctx context.Context, in *hpcAC.GetMemberJobs
|
|||
return l.GetMemberJobs(in)
|
||||
}
|
||||
|
||||
// 容器
|
||||
// 获取节点资源限额
|
||||
func (s *HpcACServer) GetNodeResources(ctx context.Context, in *hpcAC.GetNodeResourcesReq) (*hpcAC.GetNodeResourcesResp, error) {
|
||||
l := logic.NewGetNodeResourcesLogic(ctx, s.svcCtx)
|
||||
return l.GetNodeResources(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) GetInstanceServiceList(ctx context.Context, in *hpcAC.GetInstanceServiceListReq) (*hpcAC.GetInstanceServiceListResp, error) {
|
||||
l := logic.NewGetInstanceServiceListLogic(ctx, s.svcCtx)
|
||||
return l.GetInstanceServiceList(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) GetInstanceServiceDetail(ctx context.Context, in *hpcAC.GetInstanceServiceDetailReq) (*hpcAC.GetInstanceServiceDetailResp, error) {
|
||||
l := logic.NewGetInstanceServiceDetailLogic(ctx, s.svcCtx)
|
||||
return l.GetInstanceServiceDetail(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) StartInstanceService(ctx context.Context, in *hpcAC.StartInstanceServiceReq) (*hpcAC.StartInstanceServiceResp, error) {
|
||||
l := logic.NewStartInstanceServiceLogic(ctx, s.svcCtx)
|
||||
return l.StartInstanceService(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) StopInstanceService(ctx context.Context, in *hpcAC.StopInstanceServiceReq) (*hpcAC.StopInstanceServiceResp, error) {
|
||||
l := logic.NewStopInstanceServiceLogic(ctx, s.svcCtx)
|
||||
return l.StopInstanceService(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) CreateInstanceService(ctx context.Context, in *hpcAC.CreateInstanceServiceReq) (*hpcAC.CreateInstanceServiceResp, error) {
|
||||
l := logic.NewCreateInstanceServiceLogic(ctx, s.svcCtx)
|
||||
return l.CreateInstanceService(in)
|
||||
}
|
||||
|
||||
func (s *HpcACServer) GetImageList(ctx context.Context, in *hpcAC.GetImageListReq) (*hpcAC.GetImageListResp, error) {
|
||||
l := logic.NewGetImageListLogic(ctx, s.svcCtx)
|
||||
return l.GetImageList(in)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package svc
|
|||
import (
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/zeromicro/go-zero/zrpc"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/common"
|
||||
"gitlink.org.cn/JointCloud/pcm-ac/internal/config"
|
||||
"gitlink.org.cn/JointCloud/pcm-coordinator/rpc/client/participantservice"
|
||||
"gitlink.org.cn/JointCloud/pcm-coordinator/rpc/client/pcmcore"
|
||||
|
@ -14,26 +13,13 @@ type ServiceContext struct {
|
|||
Cron *cron.Cron
|
||||
PcmCoreRpc pcmcore.PcmCore
|
||||
ParticipantRpc participantservice.ParticipantService
|
||||
AuthService *common.AuthService
|
||||
AuthServiceMap map[string]*common.AuthService
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
authMap, err := common.NewAuthService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
asv, err := common.GetAuthServiceWithResource(authMap)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
Cron: cron.New(cron.WithSeconds()),
|
||||
PcmCoreRpc: pcmcore.NewPcmCore(zrpc.MustNewClient(c.PcmCoreRpcConf)),
|
||||
ParticipantRpc: participantservice.NewParticipantService(zrpc.MustNewClient(c.PcmCoreRpcConf)),
|
||||
AuthService: asv,
|
||||
AuthServiceMap: authMap,
|
||||
}
|
||||
}
|
||||
|
|
317
pb/hpcAC.proto
317
pb/hpcAC.proto
|
@ -88,7 +88,7 @@ message JobInitAttr {
|
|||
|
||||
message JobVncSessionInfo {
|
||||
string archive = 1; // @gotags: copier:"archive", json:"archive"
|
||||
int32 i_client_number = 2; // @gotags: copier:"iClientNumber", json:"iClientNumber"
|
||||
uint32 i_client_number = 2; // @gotags: copier:"iClientNumber", json:"iClientNumber"
|
||||
string i_pixel_depth = 3; // @gotags: copier:"iPixelDepth", json:"iPixelDepth"
|
||||
string list_clients = 4; // @gotags: copier:"listClients", json:"listClients"
|
||||
string locale = 5; // @gotags: copier:"locale", json:"locale"
|
||||
|
@ -111,82 +111,42 @@ message JobVncSessionInfo {
|
|||
string vnc_code = 22; // @gotags: copier:"vncCode", json:"vncCode"
|
||||
}
|
||||
|
||||
//message JobDetail {
|
||||
// string job_id = 1; // @gotags: copier:"JobId", json:"jobId"
|
||||
// string job_name = 2; // @gotags: copier:"JobName", json:"jobName"
|
||||
// string job_status = 3; // @gotags: copier:"JobStatus", json:"jobStatus"
|
||||
// string queue = 4; // @gotags: copier:"Queue", json:"queue"
|
||||
// string user = 5; // @gotags: copier:"User", json:"user"
|
||||
// string job_submit_time = 6; // @gotags: copier:"JobSubmitTime", json:"jobSubmitTime"
|
||||
// string job_start_time = 7; // @gotags: copier:"JobStartTime", json:"jobStartTime"
|
||||
// string job_end_time = 8; // @gotags: copier:"JobEndTime", json:"jobEndTime"
|
||||
// string exit_code = 9; // @gotags: copier:"ExitCode", json:"exitCode"
|
||||
// string node_used = 10; // @gotags: copier:"NodeUsed", json:"nodeUsed"
|
||||
// int32 proc_num_used = 11; // @gotags: copier:"procNumUsed", json:"procNumUsed"
|
||||
// int32 gpu_num_used = 12; // @gotags: copier:"GpuNumUsed", json:"gpuNumUsed"
|
||||
// int32 dcu_num_used = 13; // @gotags: copier:"DcuNumUsed", json:"dcuNumUsed"
|
||||
// string mem_used = 14; // @gotags: copier:"MemUsed", json:"memUsed"
|
||||
// string cpu_time_used = 15; // @gotags: copier:"CpuTimeUsed", json:"cpuTimeUsed"
|
||||
// string job_run_time = 16; // @gotags: copier:"JobRunTime", json:"jobRunTime"
|
||||
// string walltime_req = 17; // @gotags: copier:"WalltimeReq", json:"walltimeReq"
|
||||
// int32 node_num_req = 18; // @gotags: copier:"NodeNumReq", json:"nodeNumReq"
|
||||
// int32 proc_num_req = 19; // @gotags: copier:"ProcNumReq", json:"procNumReq"
|
||||
// int32 gpu_num_req = 20; // @gotags: copier:"GpuNumReq", json:"gpuNumReq"
|
||||
// int32 dcu_num_req = 21; // @gotags: copier:"DcuNumReq", json:"dcuNumReq"
|
||||
// string work_dir = 22; // @gotags: copier:"WorkDir", json:"workDir"
|
||||
// string error_path = 23; // @gotags: copier:"ErrorPath", json:"errorPath"
|
||||
// string output_path = 24; // @gotags: copier:"OutputPath", json:"outputPath"
|
||||
// string priority = 25; // @gotags: copier:"Priority", json:"priority"
|
||||
// string account = 26; // @gotags: copier:"Account", json:"account"
|
||||
// string app_type = 27; // @gotags: copier:"AppType", json:"appType"
|
||||
// string scale = 28; // @gotags: copier:"Scale", json:"scale"
|
||||
// JobVncSessionInfo job_session_info = 29; // @gotags: copier:"JobVncSessionInfo", json:"jobVncSessionInfo"
|
||||
// JobInitAttr job_init_attr = 30; // @gotags: copier:"JobInitAttr", json:"jobInitAttr"
|
||||
// string job_manager_id = 31; // @gotags: copier:"JobManagerId", json:"jobmanagerId"
|
||||
// string job_manager_name = 32; // @gotags: copier:"JobManagerName", json:"jobmanagerName"
|
||||
// string job_manager_type = 33; // @gotags: copier:"JobManagerType", json:"jobmanagerType"
|
||||
// string restarts = 34; // @gotags: copier:"Restarts", json:"restarts"
|
||||
// string ave_vm_size = 35; // @gotags: copier:"AveVMSize", json:"aveVMSize"
|
||||
// string ave_rss = 36; // @gotags: copier:"AveRSS", json:"aveRSS"
|
||||
//}
|
||||
|
||||
message JobDetail {
|
||||
string jobId = 1;
|
||||
string jobName = 2;
|
||||
string jobStatus = 3;
|
||||
string queue = 4;
|
||||
string user = 5;
|
||||
string jobSubmitTime = 6;
|
||||
string jobStartTime = 7;
|
||||
string jobEndTime = 8;
|
||||
string exitCode = 9;
|
||||
string nodeUsed = 10;
|
||||
int32 procNumUsed = 11;
|
||||
int32 gpuNumUsed = 12;
|
||||
int32 dcuNumUsed = 13;
|
||||
string memUsed = 14;
|
||||
string cpuTimeUsed = 15;
|
||||
string jobRunTime = 16;
|
||||
string walltimeReq = 17;
|
||||
int32 nodeNumReq = 18;
|
||||
int32 procNumReq = 19;
|
||||
int32 gpuNumReq = 20;
|
||||
int32 dcuNumReq = 21;
|
||||
string workDir = 22;
|
||||
string errorPath = 23;
|
||||
string outputPath = 24;
|
||||
string priority = 25;
|
||||
string account = 26;
|
||||
string appType = 27;
|
||||
string scale = 28;
|
||||
JobVncSessionInfo jobVncSessionInfo = 29;
|
||||
JobInitAttr jobInitAttr = 30;
|
||||
string jobmanagerId = 31;
|
||||
string jobmanagerName = 32;
|
||||
string jobmanagerType = 33;
|
||||
string restarts = 34;
|
||||
string aveVMSize = 35;
|
||||
string aveRSS = 36;
|
||||
string app_type = 1; // @gotags: copier:"AppType", json:"appType"
|
||||
string ave_rss = 2; // @gotags: copier:"AveRSS", json:"aveRSS"
|
||||
string ave_vm_size = 3; // @gotags: copier:"AveVMSize", json:"aveVMSize"
|
||||
string cpu_time_used = 4; // @gotags: copier:"CpuTimeUsed", json:"cpuTimeUsed"
|
||||
uint32 dcu_num_req = 5; // @gotags: copier:"DcuNumReq", json:"dcuNumReq"
|
||||
uint32 dcu_num_used = 6; // @gotags: copier:"DcuNumUsed", json:"dcuNumUsed"
|
||||
string error_path = 7; // @gotags: copier:"ErrorPath", json:"errorPath"
|
||||
string exit_code = 8; // @gotags: copier:"ExitCode", json:"exitCode"
|
||||
uint32 gpu_num_req = 9; // @gotags: copier:"GpuNumReq", json:"gpuNumReq"
|
||||
uint32 gpu_num_used = 10; // @gotags: copier:"GpuNumUsed", json:"gpuNumUsed"
|
||||
string job_end_time = 11; // @gotags: copier:"JobEndTime", json:"jobEndTime"
|
||||
string job_id = 12; // @gotags: copier:"JobId", json:"jobId"
|
||||
JobInitAttr job_init_attr = 13; // @gotags: copier:"JobInitAttr", json:"JobInitAttr"
|
||||
string job_name = 14; // @gotags: copier:"JobName", json:"jobName"
|
||||
string job_run_time = 15; // @gotags: copier:"JobRunTime", json:"jobRunTime"
|
||||
string job_start_time = 16; // @gotags: copier:"JobStartTime", json:"jobStartTime"
|
||||
string job_status = 17; // @gotags: copier:"JobStatus", json:"jobStatus"
|
||||
string job_submit_time = 18; // @gotags: copier:"JobSubmitTime", json:"jobSubmitTime"
|
||||
JobVncSessionInfo job_session_info = 19; // @gotags: copier:"JobVncSessionInfo", json:"jobVncSessionInfo"
|
||||
string job_manager_id = 20; // @gotags: copier:"JobManagerId", json:"jobmanagerId"
|
||||
string job_manager_name = 21; // @gotags: copier:"JobManagerName", json:"jobmanagerName"
|
||||
string job_manager_type = 22; // @gotags: copier:"JobManagerType", json:"jobmanagerType"
|
||||
string mem_used = 23; // @gotags: copier:"MemUsed", json:"memUsed"
|
||||
uint32 node_num_req = 24; // @gotags: copier:"NodeNumReq", json:"nodeNumReq"
|
||||
string node_used = 25; // @gotags: copier:"NodeUsed", json:"nodeUsed"
|
||||
string output_path = 26; // @gotags: copier:"OutputPath", json:"outputPath"
|
||||
string priority = 27; // @gotags: copier:"Priority", json:"priority"
|
||||
uint32 proc_num_req = 28; // @gotags: copier:"ProcNumReq", json:"procNumReq"
|
||||
uint32 proc_num_used = 29; // @gotags: copier:"procNumUsed", json:"procNumUsed"
|
||||
string queue = 30; // @gotags: copier:"Queue", json:"queue"
|
||||
string restarts = 31; // @gotags: copier:"Restarts", json:"restarts"
|
||||
string scale = 32; // @gotags: copier:"Scale", json:"scale"
|
||||
string user = 33; // @gotags: copier:"User", json:"user"
|
||||
string walltime_req = 34; // @gotags: copier:"WalltimeReq", json:"walltimeReq"
|
||||
string work_dir = 35; // @gotags: copier:"WorkDir", json:"workDir"
|
||||
}
|
||||
|
||||
message JobDetailReq {
|
||||
|
@ -718,12 +678,12 @@ message GetInstanceListResp {
|
|||
}
|
||||
|
||||
message ContainerPortInfo {
|
||||
string id = 1;
|
||||
string accessUrl = 1;
|
||||
int32 containerPort = 2;
|
||||
string createTime = 3;
|
||||
string accessUrl = 4;
|
||||
string containerServicesUrl = 5;
|
||||
string contentPath = 6;
|
||||
string containerServicesUrl = 3;
|
||||
string contentPath = 4;
|
||||
int64 createTime = 5;
|
||||
string id = 6;
|
||||
string protocolType = 7;
|
||||
}
|
||||
|
||||
|
@ -796,18 +756,6 @@ message DeleteTaskAiReq {
|
|||
string ids = 1;
|
||||
}
|
||||
|
||||
message StopTaskAiReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message StopTaskAiResp {
|
||||
string code = 1;
|
||||
StopTaskAiData data = 2;
|
||||
string msg = 3;
|
||||
}
|
||||
|
||||
message StopTaskAiData {}
|
||||
|
||||
message DeleteTaskAiResp {
|
||||
string code = 1;
|
||||
bool data = 2;
|
||||
|
@ -848,16 +796,17 @@ message GetInstanceListParams {
|
|||
string version = 31;
|
||||
}
|
||||
|
||||
message GetInferUrlReq{
|
||||
string modelName=1; //eg:imagenet_resnet50 模型+算法
|
||||
string type = 2; //eg:image 类型
|
||||
string card = 3; //eg:cpu 芯片
|
||||
message SubmitImageInferTaskReq{
|
||||
string id = 1;
|
||||
string path = 2;
|
||||
string cover = 3;
|
||||
string file = 4;
|
||||
}
|
||||
|
||||
message GetInferUrlResp{
|
||||
message SubmitImageInferTaskResp{
|
||||
string code = 1;
|
||||
string message = 2;
|
||||
string url = 3;
|
||||
string result = 3;
|
||||
}
|
||||
|
||||
message SubmitPytorchTaskParams {
|
||||
|
@ -1104,20 +1053,6 @@ message UploadFileResp {
|
|||
|
||||
message UploadFileData {
|
||||
}
|
||||
|
||||
message IsExistFileReq {
|
||||
string path = 1;
|
||||
}
|
||||
|
||||
message IsExistFileResp {
|
||||
string code = 1;
|
||||
Exist data = 2;
|
||||
string msg = 3;
|
||||
}
|
||||
|
||||
message Exist {
|
||||
bool exist = 1;
|
||||
}
|
||||
/******************曙光文件接口 End*************************/
|
||||
|
||||
/******************曙光容器 Start*************************/
|
||||
|
@ -1145,147 +1080,6 @@ message GetNodeResourcesData {
|
|||
int64 nodeNumber = 10;
|
||||
string maxTime = 11;
|
||||
}
|
||||
|
||||
message GetInstanceServiceDetailReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetInstanceServiceDetailResp {
|
||||
string code = 1;
|
||||
string msg = 2;
|
||||
InstanceService data = 3;
|
||||
}
|
||||
|
||||
message GetInstanceServiceListReq {
|
||||
GetInstanceServiceListReqParam param = 1;
|
||||
}
|
||||
|
||||
message GetInstanceServiceListReqParam {
|
||||
string instanceServiceName = 1;
|
||||
string status = 2;
|
||||
string taskType = 3;
|
||||
int32 start = 4;
|
||||
int32 limit = 5;
|
||||
string sort = 6;
|
||||
}
|
||||
|
||||
message GetInstanceServiceListResp {
|
||||
string code = 1;
|
||||
string msg = 2;
|
||||
repeated InstanceService data = 3;
|
||||
}
|
||||
|
||||
message InstanceService {
|
||||
string id = 1;
|
||||
string headerNotebookId = 2;
|
||||
string instanceServiceName = 3;
|
||||
int32 currentIndex = 4;
|
||||
int32 gpuNumber = 5;
|
||||
int32 cpuNumber = 6;
|
||||
int32 ramSize = 7;
|
||||
string acceleratorType = 8;
|
||||
string resourceGroup = 9;
|
||||
string resourceSpec = 10;
|
||||
int32 taskNumber = 11;
|
||||
string timeoutLimit = 12;
|
||||
string userName = 13;
|
||||
string version = 14;
|
||||
string imagePath = 15;
|
||||
string status = 16;
|
||||
string taskType = 17;
|
||||
string description = 18;
|
||||
string createTime = 19;
|
||||
string startTime = 20;
|
||||
string endTime = 21;
|
||||
string duration = 22;
|
||||
string remainingTime = 23;
|
||||
string tensorboardId = 24;
|
||||
string tensorboardPath = 25;
|
||||
repeated MountInfo mountInfoList = 26;
|
||||
repeated ContainerPortInfo containerPortInfoList = 27;
|
||||
bool useStartScript = 28;
|
||||
string startScriptContent = 29;
|
||||
string startScriptPath = 30;
|
||||
string startScriptActionScope = 31;
|
||||
string headerNotebookIp = 32;
|
||||
string env = 33;
|
||||
}
|
||||
|
||||
message MountInfo {
|
||||
|
||||
}
|
||||
|
||||
message StartInstanceServiceReq {
|
||||
string instanceServiceId = 1;
|
||||
}
|
||||
|
||||
message StartInstanceServiceResp {
|
||||
string code = 1;
|
||||
string msg = 2;
|
||||
string data = 3;
|
||||
}
|
||||
|
||||
message StopInstanceServiceReq {
|
||||
repeated string ids = 1;
|
||||
}
|
||||
|
||||
message StopInstanceServiceResp {
|
||||
string code = 1;
|
||||
string msg = 2;
|
||||
string data = 3;
|
||||
}
|
||||
|
||||
message CreateInstanceServiceReq {
|
||||
CreateParams data = 1;
|
||||
}
|
||||
|
||||
message CreateInstanceServiceResp {
|
||||
string code = 1;
|
||||
string msg = 2;
|
||||
string data = 3;
|
||||
}
|
||||
|
||||
message CreateParams {
|
||||
string acceleratorType = 1;
|
||||
repeated ContainerPortInfoList containerPortInfoList = 2;
|
||||
int32 cpuNumber = 3;
|
||||
string description = 4;
|
||||
int32 gpuNumber = 5;
|
||||
string imagePath = 6;
|
||||
string instanceServiceName = 7;
|
||||
repeated MountInfoList mountInfoList = 8;
|
||||
int32 ramSize = 9;
|
||||
string resourceGroup = 10;
|
||||
string startScriptActionScope = 11;
|
||||
string startScriptContent = 12;
|
||||
int32 taskNumber = 13;
|
||||
string taskType = 14;
|
||||
string timeoutLimit = 15;
|
||||
bool useStartScript = 16;
|
||||
string version = 17;
|
||||
}
|
||||
|
||||
message ContainerPortInfoList {
|
||||
int32 containerPort = 1;
|
||||
string protocolType = 2;
|
||||
}
|
||||
|
||||
message MountInfoList {
|
||||
string sourcePath = 1;
|
||||
string targetPath = 2;
|
||||
string type = 3;
|
||||
}
|
||||
|
||||
message GetImageListReq{
|
||||
string acceleratorType = 1;
|
||||
string imageType = 2;
|
||||
}
|
||||
|
||||
message GetImageListResp{
|
||||
string msg = 1;
|
||||
string code = 2;
|
||||
repeated ImageAI data = 3;
|
||||
}
|
||||
/******************曙光容器 End*************************/
|
||||
|
||||
/******************用户资源 Start*************************/
|
||||
|
@ -1405,31 +1199,24 @@ service hpcAC {
|
|||
rpc GetPytorchTask (GetPytorchTaskReq) returns (GetPytorchTaskResp);
|
||||
rpc GetTensorflowTask (GetTensorflowTaskReq) returns (GetTensorflowTaskResp);
|
||||
rpc DeleteTaskAi (DeleteTaskAiReq) returns (DeleteTaskAiResp);
|
||||
rpc StopTaskAi (StopTaskAiReq) returns (StopTaskAiResp);
|
||||
rpc GetResourceSpec (GetResourceSpecReq) returns (GetResourceSpecResp);
|
||||
rpc GetInstanceLog (GetInstanceLogReq) returns (GetInstanceLogResp);
|
||||
rpc GetInstanceListAi (GetInstanceListReq) returns (GetInstanceListResp);
|
||||
rpc GetInferUrl (GetInferUrlReq) returns(GetInferUrlResp);
|
||||
rpc SubmitImageInferTask (SubmitImageInferTaskReq) returns(SubmitImageInferTaskResp);
|
||||
|
||||
|
||||
//曙光文件接口
|
||||
rpc GetFileList (GetFileListReq) returns (GetFileListResp);
|
||||
rpc GetFile (GetFileReq) returns (GetFileResp);
|
||||
rpc UploadFile (UploadFileReq) returns (UploadFileResp);
|
||||
rpc IsExistFile (IsExistFileReq) returns (IsExistFileResp);
|
||||
|
||||
|
||||
//用户资源
|
||||
rpc GetUserInfo (GetUserInfoReq) returns (GetUserInfoResp);
|
||||
rpc GetMemberJobs (GetMemberJobsReq) returns (GetMemberJobsResp); //查询成员实时作业列表
|
||||
|
||||
//容器
|
||||
rpc GetNodeResources (GetNodeResourcesReq) returns (GetNodeResourcesResp); //获取节点资源限额
|
||||
rpc GetInstanceServiceList (GetInstanceServiceListReq) returns (GetInstanceServiceListResp);
|
||||
rpc GetInstanceServiceDetail (GetInstanceServiceDetailReq) returns (GetInstanceServiceDetailResp);
|
||||
rpc StartInstanceService (StartInstanceServiceReq) returns (StartInstanceServiceResp);
|
||||
rpc StopInstanceService (StopInstanceServiceReq) returns (StopInstanceServiceResp);
|
||||
rpc CreateInstanceService (CreateInstanceServiceReq) returns (CreateInstanceServiceResp);
|
||||
rpc GetImageList (GetImageListReq) returns (GetImageListResp);
|
||||
//获取节点资源限额
|
||||
rpc GetNodeResources (GetNodeResourcesReq) returns (GetNodeResourcesResp);
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
squirrel.test
|
|
@ -0,0 +1,30 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgresql
|
||||
|
||||
# Setting sudo access to false will let Travis CI use containers rather than
|
||||
# VMs to run the tests. For more details see:
|
||||
# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
|
||||
sudo: false
|
||||
|
||||
before_script:
|
||||
- mysql -e 'CREATE DATABASE squirrel;'
|
||||
- psql -c 'CREATE DATABASE squirrel;' -U postgres
|
||||
|
||||
script:
|
||||
- go test
|
||||
- cd integration
|
||||
- go test -args -driver sqlite3
|
||||
- go test -args -driver mysql -dataSource travis@/squirrel
|
||||
- go test -args -driver postgres -dataSource 'postgres://postgres@localhost/squirrel?sslmode=disable'
|
||||
|
||||
notifications:
|
||||
irc: "irc.freenode.net#masterminds"
|
|
@ -0,0 +1,23 @@
|
|||
MIT License
|
||||
|
||||
Squirrel: The Masterminds
|
||||
Copyright (c) 2014-2015, Lann Martin. Copyright (C) 2015-2016, Google. Copyright (C) 2015, Matt Farina and Matt Butcher.
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,142 @@
|
|||
[](https://masterminds.github.io/stability/maintenance.html)
|
||||
### Squirrel is "complete".
|
||||
Bug fixes will still be merged (slowly). Bug reports are welcome, but I will not necessarily respond to them. If another fork (or substantially similar project) actively improves on what Squirrel does, let me know and I may link to it here.
|
||||
|
||||
|
||||
# Squirrel - fluent SQL generator for Go
|
||||
|
||||
```go
|
||||
import "github.com/Masterminds/squirrel"
|
||||
```
|
||||
|
||||
|
||||
[](https://godoc.org/github.com/Masterminds/squirrel)
|
||||
[](https://travis-ci.org/Masterminds/squirrel)
|
||||
|
||||
**Squirrel is not an ORM.** For an application of Squirrel, check out
|
||||
[structable, a table-struct mapper](https://github.com/Masterminds/structable)
|
||||
|
||||
|
||||
Squirrel helps you build SQL queries from composable parts:
|
||||
|
||||
```go
|
||||
import sq "github.com/Masterminds/squirrel"
|
||||
|
||||
users := sq.Select("*").From("users").Join("emails USING (email_id)")
|
||||
|
||||
active := users.Where(sq.Eq{"deleted_at": nil})
|
||||
|
||||
sql, args, err := active.ToSql()
|
||||
|
||||
sql == "SELECT * FROM users JOIN emails USING (email_id) WHERE deleted_at IS NULL"
|
||||
```
|
||||
|
||||
```go
|
||||
sql, args, err := sq.
|
||||
Insert("users").Columns("name", "age").
|
||||
Values("moe", 13).Values("larry", sq.Expr("? + 5", 12)).
|
||||
ToSql()
|
||||
|
||||
sql == "INSERT INTO users (name,age) VALUES (?,?),(?,? + 5)"
|
||||
```
|
||||
|
||||
Squirrel can also execute queries directly:
|
||||
|
||||
```go
|
||||
stooges := users.Where(sq.Eq{"username": []string{"moe", "larry", "curly", "shemp"}})
|
||||
three_stooges := stooges.Limit(3)
|
||||
rows, err := three_stooges.RunWith(db).Query()
|
||||
|
||||
// Behaves like:
|
||||
rows, err := db.Query("SELECT * FROM users WHERE username IN (?,?,?,?) LIMIT 3",
|
||||
"moe", "larry", "curly", "shemp")
|
||||
```
|
||||
|
||||
Squirrel makes conditional query building a breeze:
|
||||
|
||||
```go
|
||||
if len(q) > 0 {
|
||||
users = users.Where("name LIKE ?", fmt.Sprint("%", q, "%"))
|
||||
}
|
||||
```
|
||||
|
||||
Squirrel wants to make your life easier:
|
||||
|
||||
```go
|
||||
// StmtCache caches Prepared Stmts for you
|
||||
dbCache := sq.NewStmtCache(db)
|
||||
|
||||
// StatementBuilder keeps your syntax neat
|
||||
mydb := sq.StatementBuilder.RunWith(dbCache)
|
||||
select_users := mydb.Select("*").From("users")
|
||||
```
|
||||
|
||||
Squirrel loves PostgreSQL:
|
||||
|
||||
```go
|
||||
psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)
|
||||
|
||||
// You use question marks for placeholders...
|
||||
sql, _, _ := psql.Select("*").From("elephants").Where("name IN (?,?)", "Dumbo", "Verna").ToSql()
|
||||
|
||||
/// ...squirrel replaces them using PlaceholderFormat.
|
||||
sql == "SELECT * FROM elephants WHERE name IN ($1,$2)"
|
||||
|
||||
|
||||
/// You can retrieve id ...
|
||||
query := sq.Insert("nodes").
|
||||
Columns("uuid", "type", "data").
|
||||
Values(node.Uuid, node.Type, node.Data).
|
||||
Suffix("RETURNING \"id\"").
|
||||
RunWith(m.db).
|
||||
PlaceholderFormat(sq.Dollar)
|
||||
|
||||
query.QueryRow().Scan(&node.id)
|
||||
```
|
||||
|
||||
You can escape question marks by inserting two question marks:
|
||||
|
||||
```sql
|
||||
SELECT * FROM nodes WHERE meta->'format' ??| array[?,?]
|
||||
```
|
||||
|
||||
will generate with the Dollar Placeholder:
|
||||
|
||||
```sql
|
||||
SELECT * FROM nodes WHERE meta->'format' ?| array[$1,$2]
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
* **How can I build an IN query on composite keys / tuples, e.g. `WHERE (col1, col2) IN ((1,2),(3,4))`? ([#104](https://github.com/Masterminds/squirrel/issues/104))**
|
||||
|
||||
Squirrel does not explicitly support tuples, but you can get the same effect with e.g.:
|
||||
|
||||
```go
|
||||
sq.Or{
|
||||
sq.Eq{"col1": 1, "col2": 2},
|
||||
sq.Eq{"col1": 3, "col2": 4}}
|
||||
```
|
||||
|
||||
```sql
|
||||
WHERE (col1 = 1 AND col2 = 2) OR (col1 = 3 AND col2 = 4)
|
||||
```
|
||||
|
||||
(which should produce the same query plan as the tuple version)
|
||||
|
||||
* **Why doesn't `Eq{"mynumber": []uint8{1,2,3}}` turn into an `IN` query? ([#114](https://github.com/Masterminds/squirrel/issues/114))**
|
||||
|
||||
Values of type `[]byte` are handled specially by `database/sql`. In Go, [`byte` is just an alias of `uint8`](https://golang.org/pkg/builtin/#byte), so there is no way to distinguish `[]uint8` from `[]byte`.
|
||||
|
||||
* **Some features are poorly documented!**
|
||||
|
||||
This isn't a frequent complaints section!
|
||||
|
||||
* **Some features are poorly documented?**
|
||||
|
||||
Yes. The tests should be considered a part of the documentation; take a look at those for ideas on how to express more complex queries.
|
||||
|
||||
## License
|
||||
|
||||
Squirrel is released under the
|
||||
[MIT License](http://www.opensource.org/licenses/MIT).
|
|
@ -0,0 +1,128 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
func init() {
|
||||
builder.Register(CaseBuilder{}, caseData{})
|
||||
}
|
||||
|
||||
// sqlizerBuffer is a helper that allows to write many Sqlizers one by one
|
||||
// without constant checks for errors that may come from Sqlizer
|
||||
type sqlizerBuffer struct {
|
||||
bytes.Buffer
|
||||
args []interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
// WriteSql converts Sqlizer to SQL strings and writes it to buffer
|
||||
func (b *sqlizerBuffer) WriteSql(item Sqlizer) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var str string
|
||||
var args []interface{}
|
||||
str, args, b.err = nestedToSql(item)
|
||||
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.WriteString(str)
|
||||
b.WriteByte(' ')
|
||||
b.args = append(b.args, args...)
|
||||
}
|
||||
|
||||
func (b *sqlizerBuffer) ToSql() (string, []interface{}, error) {
|
||||
return b.String(), b.args, b.err
|
||||
}
|
||||
|
||||
// whenPart is a helper structure to describe SQLs "WHEN ... THEN ..." expression
|
||||
type whenPart struct {
|
||||
when Sqlizer
|
||||
then Sqlizer
|
||||
}
|
||||
|
||||
func newWhenPart(when interface{}, then interface{}) whenPart {
|
||||
return whenPart{newPart(when), newPart(then)}
|
||||
}
|
||||
|
||||
// caseData holds all the data required to build a CASE SQL construct
|
||||
type caseData struct {
|
||||
What Sqlizer
|
||||
WhenParts []whenPart
|
||||
Else Sqlizer
|
||||
}
|
||||
|
||||
// ToSql implements Sqlizer
|
||||
func (d *caseData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||
if len(d.WhenParts) == 0 {
|
||||
err = errors.New("case expression must contain at lease one WHEN clause")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
sql := sqlizerBuffer{}
|
||||
|
||||
sql.WriteString("CASE ")
|
||||
if d.What != nil {
|
||||
sql.WriteSql(d.What)
|
||||
}
|
||||
|
||||
for _, p := range d.WhenParts {
|
||||
sql.WriteString("WHEN ")
|
||||
sql.WriteSql(p.when)
|
||||
sql.WriteString("THEN ")
|
||||
sql.WriteSql(p.then)
|
||||
}
|
||||
|
||||
if d.Else != nil {
|
||||
sql.WriteString("ELSE ")
|
||||
sql.WriteSql(d.Else)
|
||||
}
|
||||
|
||||
sql.WriteString("END")
|
||||
|
||||
return sql.ToSql()
|
||||
}
|
||||
|
||||
// CaseBuilder builds SQL CASE construct which could be used as parts of queries.
|
||||
type CaseBuilder builder.Builder
|
||||
|
||||
// ToSql builds the query into a SQL string and bound args.
|
||||
func (b CaseBuilder) ToSql() (string, []interface{}, error) {
|
||||
data := builder.GetStruct(b).(caseData)
|
||||
return data.ToSql()
|
||||
}
|
||||
|
||||
// MustSql builds the query into a SQL string and bound args.
|
||||
// It panics if there are any errors.
|
||||
func (b CaseBuilder) MustSql() (string, []interface{}) {
|
||||
sql, args, err := b.ToSql()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sql, args
|
||||
}
|
||||
|
||||
// what sets optional value for CASE construct "CASE [value] ..."
|
||||
func (b CaseBuilder) what(expr interface{}) CaseBuilder {
|
||||
return builder.Set(b, "What", newPart(expr)).(CaseBuilder)
|
||||
}
|
||||
|
||||
// When adds "WHEN ... THEN ..." part to CASE construct
|
||||
func (b CaseBuilder) When(when interface{}, then interface{}) CaseBuilder {
|
||||
// TODO: performance hint: replace slice of WhenPart with just slice of parts
|
||||
// where even indices of the slice belong to "when"s and odd indices belong to "then"s
|
||||
return builder.Append(b, "WhenParts", newWhenPart(when, then)).(CaseBuilder)
|
||||
}
|
||||
|
||||
// What sets optional "ELSE ..." part for CASE construct
|
||||
func (b CaseBuilder) Else(expr interface{}) CaseBuilder {
|
||||
return builder.Set(b, "Else", newPart(expr)).(CaseBuilder)
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
type deleteData struct {
|
||||
PlaceholderFormat PlaceholderFormat
|
||||
RunWith BaseRunner
|
||||
Prefixes []Sqlizer
|
||||
From string
|
||||
WhereParts []Sqlizer
|
||||
OrderBys []string
|
||||
Limit string
|
||||
Offset string
|
||||
Suffixes []Sqlizer
|
||||
}
|
||||
|
||||
func (d *deleteData) Exec() (sql.Result, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
return ExecWith(d.RunWith, d)
|
||||
}
|
||||
|
||||
func (d *deleteData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||
if len(d.From) == 0 {
|
||||
err = fmt.Errorf("delete statements must specify a From table")
|
||||
return
|
||||
}
|
||||
|
||||
sql := &bytes.Buffer{}
|
||||
|
||||
if len(d.Prefixes) > 0 {
|
||||
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sql.WriteString(" ")
|
||||
}
|
||||
|
||||
sql.WriteString("DELETE FROM ")
|
||||
sql.WriteString(d.From)
|
||||
|
||||
if len(d.WhereParts) > 0 {
|
||||
sql.WriteString(" WHERE ")
|
||||
args, err = appendToSql(d.WhereParts, sql, " AND ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.OrderBys) > 0 {
|
||||
sql.WriteString(" ORDER BY ")
|
||||
sql.WriteString(strings.Join(d.OrderBys, ", "))
|
||||
}
|
||||
|
||||
if len(d.Limit) > 0 {
|
||||
sql.WriteString(" LIMIT ")
|
||||
sql.WriteString(d.Limit)
|
||||
}
|
||||
|
||||
if len(d.Offset) > 0 {
|
||||
sql.WriteString(" OFFSET ")
|
||||
sql.WriteString(d.Offset)
|
||||
}
|
||||
|
||||
if len(d.Suffixes) > 0 {
|
||||
sql.WriteString(" ")
|
||||
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Builder
|
||||
|
||||
// DeleteBuilder builds SQL DELETE statements.
|
||||
type DeleteBuilder builder.Builder
|
||||
|
||||
func init() {
|
||||
builder.Register(DeleteBuilder{}, deleteData{})
|
||||
}
|
||||
|
||||
// Format methods
|
||||
|
||||
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
||||
// query.
|
||||
func (b DeleteBuilder) PlaceholderFormat(f PlaceholderFormat) DeleteBuilder {
|
||||
return builder.Set(b, "PlaceholderFormat", f).(DeleteBuilder)
|
||||
}
|
||||
|
||||
// Runner methods
|
||||
|
||||
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
||||
func (b DeleteBuilder) RunWith(runner BaseRunner) DeleteBuilder {
|
||||
return setRunWith(b, runner).(DeleteBuilder)
|
||||
}
|
||||
|
||||
// Exec builds and Execs the query with the Runner set by RunWith.
|
||||
func (b DeleteBuilder) Exec() (sql.Result, error) {
|
||||
data := builder.GetStruct(b).(deleteData)
|
||||
return data.Exec()
|
||||
}
|
||||
|
||||
// SQL methods
|
||||
|
||||
// ToSql builds the query into a SQL string and bound args.
|
||||
func (b DeleteBuilder) ToSql() (string, []interface{}, error) {
|
||||
data := builder.GetStruct(b).(deleteData)
|
||||
return data.ToSql()
|
||||
}
|
||||
|
||||
// MustSql builds the query into a SQL string and bound args.
|
||||
// It panics if there are any errors.
|
||||
func (b DeleteBuilder) MustSql() (string, []interface{}) {
|
||||
sql, args, err := b.ToSql()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sql, args
|
||||
}
|
||||
|
||||
// Prefix adds an expression to the beginning of the query
|
||||
func (b DeleteBuilder) Prefix(sql string, args ...interface{}) DeleteBuilder {
|
||||
return b.PrefixExpr(Expr(sql, args...))
|
||||
}
|
||||
|
||||
// PrefixExpr adds an expression to the very beginning of the query
|
||||
func (b DeleteBuilder) PrefixExpr(expr Sqlizer) DeleteBuilder {
|
||||
return builder.Append(b, "Prefixes", expr).(DeleteBuilder)
|
||||
}
|
||||
|
||||
// From sets the table to be deleted from.
|
||||
func (b DeleteBuilder) From(from string) DeleteBuilder {
|
||||
return builder.Set(b, "From", from).(DeleteBuilder)
|
||||
}
|
||||
|
||||
// Where adds WHERE expressions to the query.
|
||||
//
|
||||
// See SelectBuilder.Where for more information.
|
||||
func (b DeleteBuilder) Where(pred interface{}, args ...interface{}) DeleteBuilder {
|
||||
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(DeleteBuilder)
|
||||
}
|
||||
|
||||
// OrderBy adds ORDER BY expressions to the query.
|
||||
func (b DeleteBuilder) OrderBy(orderBys ...string) DeleteBuilder {
|
||||
return builder.Extend(b, "OrderBys", orderBys).(DeleteBuilder)
|
||||
}
|
||||
|
||||
// Limit sets a LIMIT clause on the query.
|
||||
func (b DeleteBuilder) Limit(limit uint64) DeleteBuilder {
|
||||
return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(DeleteBuilder)
|
||||
}
|
||||
|
||||
// Offset sets a OFFSET clause on the query.
|
||||
func (b DeleteBuilder) Offset(offset uint64) DeleteBuilder {
|
||||
return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(DeleteBuilder)
|
||||
}
|
||||
|
||||
// Suffix adds an expression to the end of the query
|
||||
func (b DeleteBuilder) Suffix(sql string, args ...interface{}) DeleteBuilder {
|
||||
return b.SuffixExpr(Expr(sql, args...))
|
||||
}
|
||||
|
||||
// SuffixExpr adds an expression to the end of the query
|
||||
func (b DeleteBuilder) SuffixExpr(expr Sqlizer) DeleteBuilder {
|
||||
return builder.Append(b, "Suffixes", expr).(DeleteBuilder)
|
||||
}
|
||||
|
||||
func (b DeleteBuilder) Query() (*sql.Rows, error) {
|
||||
data := builder.GetStruct(b).(deleteData)
|
||||
return data.Query()
|
||||
}
|
||||
|
||||
func (d *deleteData) Query() (*sql.Rows, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
return QueryWith(d.RunWith, d)
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// +build go1.8
|
||||
|
||||
package squirrel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
func (d *deleteData) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
ctxRunner, ok := d.RunWith.(ExecerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
return ExecContextWith(ctx, ctxRunner, d)
|
||||
}
|
||||
|
||||
func (d *deleteData) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
ctxRunner, ok := d.RunWith.(QueryerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
return QueryContextWith(ctx, ctxRunner, d)
|
||||
}
|
||||
|
||||
func (d *deleteData) QueryRowContext(ctx context.Context) RowScanner {
|
||||
if d.RunWith == nil {
|
||||
return &Row{err: RunnerNotSet}
|
||||
}
|
||||
queryRower, ok := d.RunWith.(QueryRowerContext)
|
||||
if !ok {
|
||||
if _, ok := d.RunWith.(QueryerContext); !ok {
|
||||
return &Row{err: RunnerNotQueryRunner}
|
||||
}
|
||||
return &Row{err: NoContextSupport}
|
||||
}
|
||||
return QueryRowContextWith(ctx, queryRower, d)
|
||||
}
|
||||
|
||||
// ExecContext builds and ExecContexts the query with the Runner set by RunWith.
|
||||
func (b DeleteBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||
data := builder.GetStruct(b).(deleteData)
|
||||
return data.ExecContext(ctx)
|
||||
}
|
||||
|
||||
// QueryContext builds and QueryContexts the query with the Runner set by RunWith.
|
||||
func (b DeleteBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||
data := builder.GetStruct(b).(deleteData)
|
||||
return data.QueryContext(ctx)
|
||||
}
|
||||
|
||||
// QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith.
|
||||
func (b DeleteBuilder) QueryRowContext(ctx context.Context) RowScanner {
|
||||
data := builder.GetStruct(b).(deleteData)
|
||||
return data.QueryRowContext(ctx)
|
||||
}
|
||||
|
||||
// ScanContext is a shortcut for QueryRowContext().Scan.
|
||||
func (b DeleteBuilder) ScanContext(ctx context.Context, dest ...interface{}) error {
|
||||
return b.QueryRowContext(ctx).Scan(dest...)
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// Portable true/false literals.
|
||||
sqlTrue = "(1=1)"
|
||||
sqlFalse = "(1=0)"
|
||||
)
|
||||
|
||||
type expr struct {
|
||||
sql string
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
// Expr builds an expression from a SQL fragment and arguments.
|
||||
//
|
||||
// Ex:
|
||||
// Expr("FROM_UNIXTIME(?)", t)
|
||||
func Expr(sql string, args ...interface{}) Sqlizer {
|
||||
return expr{sql: sql, args: args}
|
||||
}
|
||||
|
||||
func (e expr) ToSql() (sql string, args []interface{}, err error) {
|
||||
simple := true
|
||||
for _, arg := range e.args {
|
||||
if _, ok := arg.(Sqlizer); ok {
|
||||
simple = false
|
||||
}
|
||||
}
|
||||
if simple {
|
||||
return e.sql, e.args, nil
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
ap := e.args
|
||||
sp := e.sql
|
||||
|
||||
var isql string
|
||||
var iargs []interface{}
|
||||
|
||||
for err == nil && len(ap) > 0 && len(sp) > 0 {
|
||||
i := strings.Index(sp, "?")
|
||||
if i < 0 {
|
||||
// no more placeholders
|
||||
break
|
||||
}
|
||||
if len(sp) > i+1 && sp[i+1:i+2] == "?" {
|
||||
// escaped "??"; append it and step past
|
||||
buf.WriteString(sp[:i+2])
|
||||
sp = sp[i+2:]
|
||||
continue
|
||||
}
|
||||
|
||||
if as, ok := ap[0].(Sqlizer); ok {
|
||||
// sqlizer argument; expand it and append the result
|
||||
isql, iargs, err = as.ToSql()
|
||||
buf.WriteString(sp[:i])
|
||||
buf.WriteString(isql)
|
||||
args = append(args, iargs...)
|
||||
} else {
|
||||
// normal argument; append it and the placeholder
|
||||
buf.WriteString(sp[:i+1])
|
||||
args = append(args, ap[0])
|
||||
}
|
||||
|
||||
// step past the argument and placeholder
|
||||
ap = ap[1:]
|
||||
sp = sp[i+1:]
|
||||
}
|
||||
|
||||
// append the remaining sql and arguments
|
||||
buf.WriteString(sp)
|
||||
return buf.String(), append(args, ap...), err
|
||||
}
|
||||
|
||||
type concatExpr []interface{}
|
||||
|
||||
func (ce concatExpr) ToSql() (sql string, args []interface{}, err error) {
|
||||
for _, part := range ce {
|
||||
switch p := part.(type) {
|
||||
case string:
|
||||
sql += p
|
||||
case Sqlizer:
|
||||
pSql, pArgs, err := p.ToSql()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
sql += pSql
|
||||
args = append(args, pArgs...)
|
||||
default:
|
||||
return "", nil, fmt.Errorf("%#v is not a string or Sqlizer", part)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ConcatExpr builds an expression by concatenating strings and other expressions.
|
||||
//
|
||||
// Ex:
|
||||
// name_expr := Expr("CONCAT(?, ' ', ?)", firstName, lastName)
|
||||
// ConcatExpr("COALESCE(full_name,", name_expr, ")")
|
||||
func ConcatExpr(parts ...interface{}) concatExpr {
|
||||
return concatExpr(parts)
|
||||
}
|
||||
|
||||
// aliasExpr helps to alias part of SQL query generated with underlying "expr"
|
||||
type aliasExpr struct {
|
||||
expr Sqlizer
|
||||
alias string
|
||||
}
|
||||
|
||||
// Alias allows to define alias for column in SelectBuilder. Useful when column is
|
||||
// defined as complex expression like IF or CASE
|
||||
// Ex:
|
||||
// .Column(Alias(caseStmt, "case_column"))
|
||||
func Alias(expr Sqlizer, alias string) aliasExpr {
|
||||
return aliasExpr{expr, alias}
|
||||
}
|
||||
|
||||
func (e aliasExpr) ToSql() (sql string, args []interface{}, err error) {
|
||||
sql, args, err = e.expr.ToSql()
|
||||
if err == nil {
|
||||
sql = fmt.Sprintf("(%s) AS %s", sql, e.alias)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Eq is syntactic sugar for use with Where/Having/Set methods.
|
||||
type Eq map[string]interface{}
|
||||
|
||||
func (eq Eq) toSQL(useNotOpr bool) (sql string, args []interface{}, err error) {
|
||||
if len(eq) == 0 {
|
||||
// Empty Sql{} evaluates to true.
|
||||
sql = sqlTrue
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
exprs []string
|
||||
equalOpr = "="
|
||||
inOpr = "IN"
|
||||
nullOpr = "IS"
|
||||
inEmptyExpr = sqlFalse
|
||||
)
|
||||
|
||||
if useNotOpr {
|
||||
equalOpr = "<>"
|
||||
inOpr = "NOT IN"
|
||||
nullOpr = "IS NOT"
|
||||
inEmptyExpr = sqlTrue
|
||||
}
|
||||
|
||||
sortedKeys := getSortedKeys(eq)
|
||||
for _, key := range sortedKeys {
|
||||
var expr string
|
||||
val := eq[key]
|
||||
|
||||
switch v := val.(type) {
|
||||
case driver.Valuer:
|
||||
if val, err = v.Value(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
r := reflect.ValueOf(val)
|
||||
if r.Kind() == reflect.Ptr {
|
||||
if r.IsNil() {
|
||||
val = nil
|
||||
} else {
|
||||
val = r.Elem().Interface()
|
||||
}
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
expr = fmt.Sprintf("%s %s NULL", key, nullOpr)
|
||||
} else {
|
||||
if isListType(val) {
|
||||
valVal := reflect.ValueOf(val)
|
||||
if valVal.Len() == 0 {
|
||||
expr = inEmptyExpr
|
||||
if args == nil {
|
||||
args = []interface{}{}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < valVal.Len(); i++ {
|
||||
args = append(args, valVal.Index(i).Interface())
|
||||
}
|
||||
expr = fmt.Sprintf("%s %s (%s)", key, inOpr, Placeholders(valVal.Len()))
|
||||
}
|
||||
} else {
|
||||
expr = fmt.Sprintf("%s %s ?", key, equalOpr)
|
||||
args = append(args, val)
|
||||
}
|
||||
}
|
||||
exprs = append(exprs, expr)
|
||||
}
|
||||
sql = strings.Join(exprs, " AND ")
|
||||
return
|
||||
}
|
||||
|
||||
func (eq Eq) ToSql() (sql string, args []interface{}, err error) {
|
||||
return eq.toSQL(false)
|
||||
}
|
||||
|
||||
// NotEq is syntactic sugar for use with Where/Having/Set methods.
|
||||
// Ex:
|
||||
// .Where(NotEq{"id": 1}) == "id <> 1"
|
||||
type NotEq Eq
|
||||
|
||||
func (neq NotEq) ToSql() (sql string, args []interface{}, err error) {
|
||||
return Eq(neq).toSQL(true)
|
||||
}
|
||||
|
||||
// Like is syntactic sugar for use with LIKE conditions.
|
||||
// Ex:
|
||||
// .Where(Like{"name": "%irrel"})
|
||||
type Like map[string]interface{}
|
||||
|
||||
func (lk Like) toSql(opr string) (sql string, args []interface{}, err error) {
|
||||
var exprs []string
|
||||
for key, val := range lk {
|
||||
expr := ""
|
||||
|
||||
switch v := val.(type) {
|
||||
case driver.Valuer:
|
||||
if val, err = v.Value(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
err = fmt.Errorf("cannot use null with like operators")
|
||||
return
|
||||
} else {
|
||||
if isListType(val) {
|
||||
err = fmt.Errorf("cannot use array or slice with like operators")
|
||||
return
|
||||
} else {
|
||||
expr = fmt.Sprintf("%s %s ?", key, opr)
|
||||
args = append(args, val)
|
||||
}
|
||||
}
|
||||
exprs = append(exprs, expr)
|
||||
}
|
||||
sql = strings.Join(exprs, " AND ")
|
||||
return
|
||||
}
|
||||
|
||||
func (lk Like) ToSql() (sql string, args []interface{}, err error) {
|
||||
return lk.toSql("LIKE")
|
||||
}
|
||||
|
||||
// NotLike is syntactic sugar for use with LIKE conditions.
|
||||
// Ex:
|
||||
// .Where(NotLike{"name": "%irrel"})
|
||||
type NotLike Like
|
||||
|
||||
func (nlk NotLike) ToSql() (sql string, args []interface{}, err error) {
|
||||
return Like(nlk).toSql("NOT LIKE")
|
||||
}
|
||||
|
||||
// ILike is syntactic sugar for use with ILIKE conditions.
|
||||
// Ex:
|
||||
// .Where(ILike{"name": "sq%"})
|
||||
type ILike Like
|
||||
|
||||
func (ilk ILike) ToSql() (sql string, args []interface{}, err error) {
|
||||
return Like(ilk).toSql("ILIKE")
|
||||
}
|
||||
|
||||
// NotILike is syntactic sugar for use with ILIKE conditions.
|
||||
// Ex:
|
||||
// .Where(NotILike{"name": "sq%"})
|
||||
type NotILike Like
|
||||
|
||||
func (nilk NotILike) ToSql() (sql string, args []interface{}, err error) {
|
||||
return Like(nilk).toSql("NOT ILIKE")
|
||||
}
|
||||
|
||||
// Lt is syntactic sugar for use with Where/Having/Set methods.
|
||||
// Ex:
|
||||
// .Where(Lt{"id": 1})
|
||||
type Lt map[string]interface{}
|
||||
|
||||
func (lt Lt) toSql(opposite, orEq bool) (sql string, args []interface{}, err error) {
|
||||
var (
|
||||
exprs []string
|
||||
opr = "<"
|
||||
)
|
||||
|
||||
if opposite {
|
||||
opr = ">"
|
||||
}
|
||||
|
||||
if orEq {
|
||||
opr = fmt.Sprintf("%s%s", opr, "=")
|
||||
}
|
||||
|
||||
sortedKeys := getSortedKeys(lt)
|
||||
for _, key := range sortedKeys {
|
||||
var expr string
|
||||
val := lt[key]
|
||||
|
||||
switch v := val.(type) {
|
||||
case driver.Valuer:
|
||||
if val, err = v.Value(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
err = fmt.Errorf("cannot use null with less than or greater than operators")
|
||||
return
|
||||
}
|
||||
if isListType(val) {
|
||||
err = fmt.Errorf("cannot use array or slice with less than or greater than operators")
|
||||
return
|
||||
}
|
||||
expr = fmt.Sprintf("%s %s ?", key, opr)
|
||||
args = append(args, val)
|
||||
|
||||
exprs = append(exprs, expr)
|
||||
}
|
||||
sql = strings.Join(exprs, " AND ")
|
||||
return
|
||||
}
|
||||
|
||||
func (lt Lt) ToSql() (sql string, args []interface{}, err error) {
|
||||
return lt.toSql(false, false)
|
||||
}
|
||||
|
||||
// LtOrEq is syntactic sugar for use with Where/Having/Set methods.
|
||||
// Ex:
|
||||
// .Where(LtOrEq{"id": 1}) == "id <= 1"
|
||||
type LtOrEq Lt
|
||||
|
||||
func (ltOrEq LtOrEq) ToSql() (sql string, args []interface{}, err error) {
|
||||
return Lt(ltOrEq).toSql(false, true)
|
||||
}
|
||||
|
||||
// Gt is syntactic sugar for use with Where/Having/Set methods.
|
||||
// Ex:
|
||||
// .Where(Gt{"id": 1}) == "id > 1"
|
||||
type Gt Lt
|
||||
|
||||
func (gt Gt) ToSql() (sql string, args []interface{}, err error) {
|
||||
return Lt(gt).toSql(true, false)
|
||||
}
|
||||
|
||||
// GtOrEq is syntactic sugar for use with Where/Having/Set methods.
|
||||
// Ex:
|
||||
// .Where(GtOrEq{"id": 1}) == "id >= 1"
|
||||
type GtOrEq Lt
|
||||
|
||||
func (gtOrEq GtOrEq) ToSql() (sql string, args []interface{}, err error) {
|
||||
return Lt(gtOrEq).toSql(true, true)
|
||||
}
|
||||
|
||||
type conj []Sqlizer
|
||||
|
||||
func (c conj) join(sep, defaultExpr string) (sql string, args []interface{}, err error) {
|
||||
if len(c) == 0 {
|
||||
return defaultExpr, []interface{}{}, nil
|
||||
}
|
||||
var sqlParts []string
|
||||
for _, sqlizer := range c {
|
||||
partSQL, partArgs, err := nestedToSql(sqlizer)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if partSQL != "" {
|
||||
sqlParts = append(sqlParts, partSQL)
|
||||
args = append(args, partArgs...)
|
||||
}
|
||||
}
|
||||
if len(sqlParts) > 0 {
|
||||
sql = fmt.Sprintf("(%s)", strings.Join(sqlParts, sep))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// And conjunction Sqlizers
|
||||
type And conj
|
||||
|
||||
func (a And) ToSql() (string, []interface{}, error) {
|
||||
return conj(a).join(" AND ", sqlTrue)
|
||||
}
|
||||
|
||||
// Or conjunction Sqlizers
|
||||
type Or conj
|
||||
|
||||
func (o Or) ToSql() (string, []interface{}, error) {
|
||||
return conj(o).join(" OR ", sqlFalse)
|
||||
}
|
||||
|
||||
func getSortedKeys(exp map[string]interface{}) []string {
|
||||
sortedKeys := make([]string, 0, len(exp))
|
||||
for k := range exp {
|
||||
sortedKeys = append(sortedKeys, k)
|
||||
}
|
||||
sort.Strings(sortedKeys)
|
||||
return sortedKeys
|
||||
}
|
||||
|
||||
func isListType(val interface{}) bool {
|
||||
if driver.IsValue(val) {
|
||||
return false
|
||||
}
|
||||
valVal := reflect.ValueOf(val)
|
||||
return valVal.Kind() == reflect.Array || valVal.Kind() == reflect.Slice
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
type insertData struct {
|
||||
PlaceholderFormat PlaceholderFormat
|
||||
RunWith BaseRunner
|
||||
Prefixes []Sqlizer
|
||||
StatementKeyword string
|
||||
Options []string
|
||||
Into string
|
||||
Columns []string
|
||||
Values [][]interface{}
|
||||
Suffixes []Sqlizer
|
||||
Select *SelectBuilder
|
||||
}
|
||||
|
||||
func (d *insertData) Exec() (sql.Result, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
return ExecWith(d.RunWith, d)
|
||||
}
|
||||
|
||||
func (d *insertData) Query() (*sql.Rows, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
return QueryWith(d.RunWith, d)
|
||||
}
|
||||
|
||||
func (d *insertData) QueryRow() RowScanner {
|
||||
if d.RunWith == nil {
|
||||
return &Row{err: RunnerNotSet}
|
||||
}
|
||||
queryRower, ok := d.RunWith.(QueryRower)
|
||||
if !ok {
|
||||
return &Row{err: RunnerNotQueryRunner}
|
||||
}
|
||||
return QueryRowWith(queryRower, d)
|
||||
}
|
||||
|
||||
func (d *insertData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||
if len(d.Into) == 0 {
|
||||
err = errors.New("insert statements must specify a table")
|
||||
return
|
||||
}
|
||||
if len(d.Values) == 0 && d.Select == nil {
|
||||
err = errors.New("insert statements must have at least one set of values or select clause")
|
||||
return
|
||||
}
|
||||
|
||||
sql := &bytes.Buffer{}
|
||||
|
||||
if len(d.Prefixes) > 0 {
|
||||
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sql.WriteString(" ")
|
||||
}
|
||||
|
||||
if d.StatementKeyword == "" {
|
||||
sql.WriteString("INSERT ")
|
||||
} else {
|
||||
sql.WriteString(d.StatementKeyword)
|
||||
sql.WriteString(" ")
|
||||
}
|
||||
|
||||
if len(d.Options) > 0 {
|
||||
sql.WriteString(strings.Join(d.Options, " "))
|
||||
sql.WriteString(" ")
|
||||
}
|
||||
|
||||
sql.WriteString("INTO ")
|
||||
sql.WriteString(d.Into)
|
||||
sql.WriteString(" ")
|
||||
|
||||
if len(d.Columns) > 0 {
|
||||
sql.WriteString("(")
|
||||
sql.WriteString(strings.Join(d.Columns, ","))
|
||||
sql.WriteString(") ")
|
||||
}
|
||||
|
||||
if d.Select != nil {
|
||||
args, err = d.appendSelectToSQL(sql, args)
|
||||
} else {
|
||||
args, err = d.appendValuesToSQL(sql, args)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(d.Suffixes) > 0 {
|
||||
sql.WriteString(" ")
|
||||
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String())
|
||||
return
|
||||
}
|
||||
|
||||
func (d *insertData) appendValuesToSQL(w io.Writer, args []interface{}) ([]interface{}, error) {
|
||||
if len(d.Values) == 0 {
|
||||
return args, errors.New("values for insert statements are not set")
|
||||
}
|
||||
|
||||
io.WriteString(w, "VALUES ")
|
||||
|
||||
valuesStrings := make([]string, len(d.Values))
|
||||
for r, row := range d.Values {
|
||||
valueStrings := make([]string, len(row))
|
||||
for v, val := range row {
|
||||
if vs, ok := val.(Sqlizer); ok {
|
||||
vsql, vargs, err := vs.ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueStrings[v] = vsql
|
||||
args = append(args, vargs...)
|
||||
} else {
|
||||
valueStrings[v] = "?"
|
||||
args = append(args, val)
|
||||
}
|
||||
}
|
||||
valuesStrings[r] = fmt.Sprintf("(%s)", strings.Join(valueStrings, ","))
|
||||
}
|
||||
|
||||
io.WriteString(w, strings.Join(valuesStrings, ","))
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (d *insertData) appendSelectToSQL(w io.Writer, args []interface{}) ([]interface{}, error) {
|
||||
if d.Select == nil {
|
||||
return args, errors.New("select clause for insert statements are not set")
|
||||
}
|
||||
|
||||
selectClause, sArgs, err := d.Select.ToSql()
|
||||
if err != nil {
|
||||
return args, err
|
||||
}
|
||||
|
||||
io.WriteString(w, selectClause)
|
||||
args = append(args, sArgs...)
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// Builder
|
||||
|
||||
// InsertBuilder builds SQL INSERT statements.
|
||||
type InsertBuilder builder.Builder
|
||||
|
||||
func init() {
|
||||
builder.Register(InsertBuilder{}, insertData{})
|
||||
}
|
||||
|
||||
// Format methods
|
||||
|
||||
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
||||
// query.
|
||||
func (b InsertBuilder) PlaceholderFormat(f PlaceholderFormat) InsertBuilder {
|
||||
return builder.Set(b, "PlaceholderFormat", f).(InsertBuilder)
|
||||
}
|
||||
|
||||
// Runner methods
|
||||
|
||||
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
||||
func (b InsertBuilder) RunWith(runner BaseRunner) InsertBuilder {
|
||||
return setRunWith(b, runner).(InsertBuilder)
|
||||
}
|
||||
|
||||
// Exec builds and Execs the query with the Runner set by RunWith.
|
||||
func (b InsertBuilder) Exec() (sql.Result, error) {
|
||||
data := builder.GetStruct(b).(insertData)
|
||||
return data.Exec()
|
||||
}
|
||||
|
||||
// Query builds and Querys the query with the Runner set by RunWith.
|
||||
func (b InsertBuilder) Query() (*sql.Rows, error) {
|
||||
data := builder.GetStruct(b).(insertData)
|
||||
return data.Query()
|
||||
}
|
||||
|
||||
// QueryRow builds and QueryRows the query with the Runner set by RunWith.
|
||||
func (b InsertBuilder) QueryRow() RowScanner {
|
||||
data := builder.GetStruct(b).(insertData)
|
||||
return data.QueryRow()
|
||||
}
|
||||
|
||||
// Scan is a shortcut for QueryRow().Scan.
|
||||
func (b InsertBuilder) Scan(dest ...interface{}) error {
|
||||
return b.QueryRow().Scan(dest...)
|
||||
}
|
||||
|
||||
// SQL methods
|
||||
|
||||
// ToSql builds the query into a SQL string and bound args.
|
||||
func (b InsertBuilder) ToSql() (string, []interface{}, error) {
|
||||
data := builder.GetStruct(b).(insertData)
|
||||
return data.ToSql()
|
||||
}
|
||||
|
||||
// MustSql builds the query into a SQL string and bound args.
|
||||
// It panics if there are any errors.
|
||||
func (b InsertBuilder) MustSql() (string, []interface{}) {
|
||||
sql, args, err := b.ToSql()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sql, args
|
||||
}
|
||||
|
||||
// Prefix adds an expression to the beginning of the query
|
||||
func (b InsertBuilder) Prefix(sql string, args ...interface{}) InsertBuilder {
|
||||
return b.PrefixExpr(Expr(sql, args...))
|
||||
}
|
||||
|
||||
// PrefixExpr adds an expression to the very beginning of the query
|
||||
func (b InsertBuilder) PrefixExpr(expr Sqlizer) InsertBuilder {
|
||||
return builder.Append(b, "Prefixes", expr).(InsertBuilder)
|
||||
}
|
||||
|
||||
// Options adds keyword options before the INTO clause of the query.
|
||||
func (b InsertBuilder) Options(options ...string) InsertBuilder {
|
||||
return builder.Extend(b, "Options", options).(InsertBuilder)
|
||||
}
|
||||
|
||||
// Into sets the INTO clause of the query.
|
||||
func (b InsertBuilder) Into(from string) InsertBuilder {
|
||||
return builder.Set(b, "Into", from).(InsertBuilder)
|
||||
}
|
||||
|
||||
// Columns adds insert columns to the query.
|
||||
func (b InsertBuilder) Columns(columns ...string) InsertBuilder {
|
||||
return builder.Extend(b, "Columns", columns).(InsertBuilder)
|
||||
}
|
||||
|
||||
// Values adds a single row's values to the query.
|
||||
func (b InsertBuilder) Values(values ...interface{}) InsertBuilder {
|
||||
return builder.Append(b, "Values", values).(InsertBuilder)
|
||||
}
|
||||
|
||||
// Suffix adds an expression to the end of the query
|
||||
func (b InsertBuilder) Suffix(sql string, args ...interface{}) InsertBuilder {
|
||||
return b.SuffixExpr(Expr(sql, args...))
|
||||
}
|
||||
|
||||
// SuffixExpr adds an expression to the end of the query
|
||||
func (b InsertBuilder) SuffixExpr(expr Sqlizer) InsertBuilder {
|
||||
return builder.Append(b, "Suffixes", expr).(InsertBuilder)
|
||||
}
|
||||
|
||||
// SetMap set columns and values for insert builder from a map of column name and value
|
||||
// note that it will reset all previous columns and values was set if any
|
||||
func (b InsertBuilder) SetMap(clauses map[string]interface{}) InsertBuilder {
|
||||
// Keep the columns in a consistent order by sorting the column key string.
|
||||
cols := make([]string, 0, len(clauses))
|
||||
for col := range clauses {
|
||||
cols = append(cols, col)
|
||||
}
|
||||
sort.Strings(cols)
|
||||
|
||||
vals := make([]interface{}, 0, len(clauses))
|
||||
for _, col := range cols {
|
||||
vals = append(vals, clauses[col])
|
||||
}
|
||||
|
||||
b = builder.Set(b, "Columns", cols).(InsertBuilder)
|
||||
b = builder.Set(b, "Values", [][]interface{}{vals}).(InsertBuilder)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Select set Select clause for insert query
|
||||
// If Values and Select are used, then Select has higher priority
|
||||
func (b InsertBuilder) Select(sb SelectBuilder) InsertBuilder {
|
||||
return builder.Set(b, "Select", &sb).(InsertBuilder)
|
||||
}
|
||||
|
||||
func (b InsertBuilder) statementKeyword(keyword string) InsertBuilder {
|
||||
return builder.Set(b, "StatementKeyword", keyword).(InsertBuilder)
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// +build go1.8
|
||||
|
||||
package squirrel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
func (d *insertData) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
ctxRunner, ok := d.RunWith.(ExecerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
return ExecContextWith(ctx, ctxRunner, d)
|
||||
}
|
||||
|
||||
func (d *insertData) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
ctxRunner, ok := d.RunWith.(QueryerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
return QueryContextWith(ctx, ctxRunner, d)
|
||||
}
|
||||
|
||||
func (d *insertData) QueryRowContext(ctx context.Context) RowScanner {
|
||||
if d.RunWith == nil {
|
||||
return &Row{err: RunnerNotSet}
|
||||
}
|
||||
queryRower, ok := d.RunWith.(QueryRowerContext)
|
||||
if !ok {
|
||||
if _, ok := d.RunWith.(QueryerContext); !ok {
|
||||
return &Row{err: RunnerNotQueryRunner}
|
||||
}
|
||||
return &Row{err: NoContextSupport}
|
||||
}
|
||||
return QueryRowContextWith(ctx, queryRower, d)
|
||||
}
|
||||
|
||||
// ExecContext builds and ExecContexts the query with the Runner set by RunWith.
|
||||
func (b InsertBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||
data := builder.GetStruct(b).(insertData)
|
||||
return data.ExecContext(ctx)
|
||||
}
|
||||
|
||||
// QueryContext builds and QueryContexts the query with the Runner set by RunWith.
|
||||
func (b InsertBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||
data := builder.GetStruct(b).(insertData)
|
||||
return data.QueryContext(ctx)
|
||||
}
|
||||
|
||||
// QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith.
|
||||
func (b InsertBuilder) QueryRowContext(ctx context.Context) RowScanner {
|
||||
data := builder.GetStruct(b).(insertData)
|
||||
return data.QueryRowContext(ctx)
|
||||
}
|
||||
|
||||
// ScanContext is a shortcut for QueryRowContext().Scan.
|
||||
func (b InsertBuilder) ScanContext(ctx context.Context, dest ...interface{}) error {
|
||||
return b.QueryRowContext(ctx).Scan(dest...)
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type part struct {
|
||||
pred interface{}
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
func newPart(pred interface{}, args ...interface{}) Sqlizer {
|
||||
return &part{pred, args}
|
||||
}
|
||||
|
||||
func (p part) ToSql() (sql string, args []interface{}, err error) {
|
||||
switch pred := p.pred.(type) {
|
||||
case nil:
|
||||
// no-op
|
||||
case Sqlizer:
|
||||
sql, args, err = nestedToSql(pred)
|
||||
case string:
|
||||
sql = pred
|
||||
args = p.args
|
||||
default:
|
||||
err = fmt.Errorf("expected string or Sqlizer, not %T", pred)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func nestedToSql(s Sqlizer) (string, []interface{}, error) {
|
||||
if raw, ok := s.(rawSqlizer); ok {
|
||||
return raw.toSqlRaw()
|
||||
} else {
|
||||
return s.ToSql()
|
||||
}
|
||||
}
|
||||
|
||||
func appendToSql(parts []Sqlizer, w io.Writer, sep string, args []interface{}) ([]interface{}, error) {
|
||||
for i, p := range parts {
|
||||
partSql, partArgs, err := nestedToSql(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(partSql) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
_, err := io.WriteString(w, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = io.WriteString(w, partSql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = append(args, partArgs...)
|
||||
}
|
||||
return args, nil
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PlaceholderFormat is the interface that wraps the ReplacePlaceholders method.
|
||||
//
|
||||
// ReplacePlaceholders takes a SQL statement and replaces each question mark
|
||||
// placeholder with a (possibly different) SQL placeholder.
|
||||
type PlaceholderFormat interface {
|
||||
ReplacePlaceholders(sql string) (string, error)
|
||||
}
|
||||
|
||||
type placeholderDebugger interface {
|
||||
debugPlaceholder() string
|
||||
}
|
||||
|
||||
var (
|
||||
// Question is a PlaceholderFormat instance that leaves placeholders as
|
||||
// question marks.
|
||||
Question = questionFormat{}
|
||||
|
||||
// Dollar is a PlaceholderFormat instance that replaces placeholders with
|
||||
// dollar-prefixed positional placeholders (e.g. $1, $2, $3).
|
||||
Dollar = dollarFormat{}
|
||||
|
||||
// Colon is a PlaceholderFormat instance that replaces placeholders with
|
||||
// colon-prefixed positional placeholders (e.g. :1, :2, :3).
|
||||
Colon = colonFormat{}
|
||||
|
||||
// AtP is a PlaceholderFormat instance that replaces placeholders with
|
||||
// "@p"-prefixed positional placeholders (e.g. @p1, @p2, @p3).
|
||||
AtP = atpFormat{}
|
||||
)
|
||||
|
||||
type questionFormat struct{}
|
||||
|
||||
func (questionFormat) ReplacePlaceholders(sql string) (string, error) {
|
||||
return sql, nil
|
||||
}
|
||||
|
||||
func (questionFormat) debugPlaceholder() string {
|
||||
return "?"
|
||||
}
|
||||
|
||||
type dollarFormat struct{}
|
||||
|
||||
func (dollarFormat) ReplacePlaceholders(sql string) (string, error) {
|
||||
return replacePositionalPlaceholders(sql, "$")
|
||||
}
|
||||
|
||||
func (dollarFormat) debugPlaceholder() string {
|
||||
return "$"
|
||||
}
|
||||
|
||||
type colonFormat struct{}
|
||||
|
||||
func (colonFormat) ReplacePlaceholders(sql string) (string, error) {
|
||||
return replacePositionalPlaceholders(sql, ":")
|
||||
}
|
||||
|
||||
func (colonFormat) debugPlaceholder() string {
|
||||
return ":"
|
||||
}
|
||||
|
||||
type atpFormat struct{}
|
||||
|
||||
func (atpFormat) ReplacePlaceholders(sql string) (string, error) {
|
||||
return replacePositionalPlaceholders(sql, "@p")
|
||||
}
|
||||
|
||||
func (atpFormat) debugPlaceholder() string {
|
||||
return "@p"
|
||||
}
|
||||
|
||||
// Placeholders returns a string with count ? placeholders joined with commas.
|
||||
func Placeholders(count int) string {
|
||||
if count < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.Repeat(",?", count)[1:]
|
||||
}
|
||||
|
||||
func replacePositionalPlaceholders(sql, prefix string) (string, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
i := 0
|
||||
for {
|
||||
p := strings.Index(sql, "?")
|
||||
if p == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
if len(sql[p:]) > 1 && sql[p:p+2] == "??" { // escape ?? => ?
|
||||
buf.WriteString(sql[:p])
|
||||
buf.WriteString("?")
|
||||
if len(sql[p:]) == 1 {
|
||||
break
|
||||
}
|
||||
sql = sql[p+2:]
|
||||
} else {
|
||||
i++
|
||||
buf.WriteString(sql[:p])
|
||||
fmt.Fprintf(buf, "%s%d", prefix, i)
|
||||
sql = sql[p+1:]
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(sql)
|
||||
return buf.String(), nil
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package squirrel
|
||||
|
||||
// RowScanner is the interface that wraps the Scan method.
|
||||
//
|
||||
// Scan behaves like database/sql.Row.Scan.
|
||||
type RowScanner interface {
|
||||
Scan(...interface{}) error
|
||||
}
|
||||
|
||||
// Row wraps database/sql.Row to let squirrel return new errors on Scan.
|
||||
type Row struct {
|
||||
RowScanner
|
||||
err error
|
||||
}
|
||||
|
||||
// Scan returns Row.err or calls RowScanner.Scan.
|
||||
func (r *Row) Scan(dest ...interface{}) error {
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
return r.RowScanner.Scan(dest...)
|
||||
}
|
|
@ -0,0 +1,403 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
type selectData struct {
|
||||
PlaceholderFormat PlaceholderFormat
|
||||
RunWith BaseRunner
|
||||
Prefixes []Sqlizer
|
||||
Options []string
|
||||
Columns []Sqlizer
|
||||
From Sqlizer
|
||||
Joins []Sqlizer
|
||||
WhereParts []Sqlizer
|
||||
GroupBys []string
|
||||
HavingParts []Sqlizer
|
||||
OrderByParts []Sqlizer
|
||||
Limit string
|
||||
Offset string
|
||||
Suffixes []Sqlizer
|
||||
}
|
||||
|
||||
func (d *selectData) Exec() (sql.Result, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
return ExecWith(d.RunWith, d)
|
||||
}
|
||||
|
||||
func (d *selectData) Query() (*sql.Rows, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
return QueryWith(d.RunWith, d)
|
||||
}
|
||||
|
||||
func (d *selectData) QueryRow() RowScanner {
|
||||
if d.RunWith == nil {
|
||||
return &Row{err: RunnerNotSet}
|
||||
}
|
||||
queryRower, ok := d.RunWith.(QueryRower)
|
||||
if !ok {
|
||||
return &Row{err: RunnerNotQueryRunner}
|
||||
}
|
||||
return QueryRowWith(queryRower, d)
|
||||
}
|
||||
|
||||
func (d *selectData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||
sqlStr, args, err = d.toSqlRaw()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sqlStr)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *selectData) toSqlRaw() (sqlStr string, args []interface{}, err error) {
|
||||
if len(d.Columns) == 0 {
|
||||
err = fmt.Errorf("select statements must have at least one result column")
|
||||
return
|
||||
}
|
||||
|
||||
sql := &bytes.Buffer{}
|
||||
|
||||
if len(d.Prefixes) > 0 {
|
||||
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sql.WriteString(" ")
|
||||
}
|
||||
|
||||
sql.WriteString("SELECT ")
|
||||
|
||||
if len(d.Options) > 0 {
|
||||
sql.WriteString(strings.Join(d.Options, " "))
|
||||
sql.WriteString(" ")
|
||||
}
|
||||
|
||||
if len(d.Columns) > 0 {
|
||||
args, err = appendToSql(d.Columns, sql, ", ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if d.From != nil {
|
||||
sql.WriteString(" FROM ")
|
||||
args, err = appendToSql([]Sqlizer{d.From}, sql, "", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.Joins) > 0 {
|
||||
sql.WriteString(" ")
|
||||
args, err = appendToSql(d.Joins, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.WhereParts) > 0 {
|
||||
sql.WriteString(" WHERE ")
|
||||
args, err = appendToSql(d.WhereParts, sql, " AND ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.GroupBys) > 0 {
|
||||
sql.WriteString(" GROUP BY ")
|
||||
sql.WriteString(strings.Join(d.GroupBys, ", "))
|
||||
}
|
||||
|
||||
if len(d.HavingParts) > 0 {
|
||||
sql.WriteString(" HAVING ")
|
||||
args, err = appendToSql(d.HavingParts, sql, " AND ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.OrderByParts) > 0 {
|
||||
sql.WriteString(" ORDER BY ")
|
||||
args, err = appendToSql(d.OrderByParts, sql, ", ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.Limit) > 0 {
|
||||
sql.WriteString(" LIMIT ")
|
||||
sql.WriteString(d.Limit)
|
||||
}
|
||||
|
||||
if len(d.Offset) > 0 {
|
||||
sql.WriteString(" OFFSET ")
|
||||
sql.WriteString(d.Offset)
|
||||
}
|
||||
|
||||
if len(d.Suffixes) > 0 {
|
||||
sql.WriteString(" ")
|
||||
|
||||
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sqlStr = sql.String()
|
||||
return
|
||||
}
|
||||
|
||||
// Builder
|
||||
|
||||
// SelectBuilder builds SQL SELECT statements.
|
||||
type SelectBuilder builder.Builder
|
||||
|
||||
func init() {
|
||||
builder.Register(SelectBuilder{}, selectData{})
|
||||
}
|
||||
|
||||
// Format methods
|
||||
|
||||
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
||||
// query.
|
||||
func (b SelectBuilder) PlaceholderFormat(f PlaceholderFormat) SelectBuilder {
|
||||
return builder.Set(b, "PlaceholderFormat", f).(SelectBuilder)
|
||||
}
|
||||
|
||||
// Runner methods
|
||||
|
||||
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
||||
// For most cases runner will be a database connection.
|
||||
//
|
||||
// Internally we use this to mock out the database connection for testing.
|
||||
func (b SelectBuilder) RunWith(runner BaseRunner) SelectBuilder {
|
||||
return setRunWith(b, runner).(SelectBuilder)
|
||||
}
|
||||
|
||||
// Exec builds and Execs the query with the Runner set by RunWith.
|
||||
func (b SelectBuilder) Exec() (sql.Result, error) {
|
||||
data := builder.GetStruct(b).(selectData)
|
||||
return data.Exec()
|
||||
}
|
||||
|
||||
// Query builds and Querys the query with the Runner set by RunWith.
|
||||
func (b SelectBuilder) Query() (*sql.Rows, error) {
|
||||
data := builder.GetStruct(b).(selectData)
|
||||
return data.Query()
|
||||
}
|
||||
|
||||
// QueryRow builds and QueryRows the query with the Runner set by RunWith.
|
||||
func (b SelectBuilder) QueryRow() RowScanner {
|
||||
data := builder.GetStruct(b).(selectData)
|
||||
return data.QueryRow()
|
||||
}
|
||||
|
||||
// Scan is a shortcut for QueryRow().Scan.
|
||||
func (b SelectBuilder) Scan(dest ...interface{}) error {
|
||||
return b.QueryRow().Scan(dest...)
|
||||
}
|
||||
|
||||
// SQL methods
|
||||
|
||||
// ToSql builds the query into a SQL string and bound args.
|
||||
func (b SelectBuilder) ToSql() (string, []interface{}, error) {
|
||||
data := builder.GetStruct(b).(selectData)
|
||||
return data.ToSql()
|
||||
}
|
||||
|
||||
func (b SelectBuilder) toSqlRaw() (string, []interface{}, error) {
|
||||
data := builder.GetStruct(b).(selectData)
|
||||
return data.toSqlRaw()
|
||||
}
|
||||
|
||||
// MustSql builds the query into a SQL string and bound args.
|
||||
// It panics if there are any errors.
|
||||
func (b SelectBuilder) MustSql() (string, []interface{}) {
|
||||
sql, args, err := b.ToSql()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sql, args
|
||||
}
|
||||
|
||||
// Prefix adds an expression to the beginning of the query
|
||||
func (b SelectBuilder) Prefix(sql string, args ...interface{}) SelectBuilder {
|
||||
return b.PrefixExpr(Expr(sql, args...))
|
||||
}
|
||||
|
||||
// PrefixExpr adds an expression to the very beginning of the query
|
||||
func (b SelectBuilder) PrefixExpr(expr Sqlizer) SelectBuilder {
|
||||
return builder.Append(b, "Prefixes", expr).(SelectBuilder)
|
||||
}
|
||||
|
||||
// Distinct adds a DISTINCT clause to the query.
|
||||
func (b SelectBuilder) Distinct() SelectBuilder {
|
||||
return b.Options("DISTINCT")
|
||||
}
|
||||
|
||||
// Options adds select option to the query
|
||||
func (b SelectBuilder) Options(options ...string) SelectBuilder {
|
||||
return builder.Extend(b, "Options", options).(SelectBuilder)
|
||||
}
|
||||
|
||||
// Columns adds result columns to the query.
|
||||
func (b SelectBuilder) Columns(columns ...string) SelectBuilder {
|
||||
parts := make([]interface{}, 0, len(columns))
|
||||
for _, str := range columns {
|
||||
parts = append(parts, newPart(str))
|
||||
}
|
||||
return builder.Extend(b, "Columns", parts).(SelectBuilder)
|
||||
}
|
||||
|
||||
// RemoveColumns remove all columns from query.
|
||||
// Must add a new column with Column or Columns methods, otherwise
|
||||
// return a error.
|
||||
func (b SelectBuilder) RemoveColumns() SelectBuilder {
|
||||
return builder.Delete(b, "Columns").(SelectBuilder)
|
||||
}
|
||||
|
||||
// Column adds a result column to the query.
|
||||
// Unlike Columns, Column accepts args which will be bound to placeholders in
|
||||
// the columns string, for example:
|
||||
// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3)
|
||||
func (b SelectBuilder) Column(column interface{}, args ...interface{}) SelectBuilder {
|
||||
return builder.Append(b, "Columns", newPart(column, args...)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// From sets the FROM clause of the query.
|
||||
func (b SelectBuilder) From(from string) SelectBuilder {
|
||||
return builder.Set(b, "From", newPart(from)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// FromSelect sets a subquery into the FROM clause of the query.
|
||||
func (b SelectBuilder) FromSelect(from SelectBuilder, alias string) SelectBuilder {
|
||||
// Prevent misnumbered parameters in nested selects (#183).
|
||||
from = from.PlaceholderFormat(Question)
|
||||
return builder.Set(b, "From", Alias(from, alias)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// JoinClause adds a join clause to the query.
|
||||
func (b SelectBuilder) JoinClause(pred interface{}, args ...interface{}) SelectBuilder {
|
||||
return builder.Append(b, "Joins", newPart(pred, args...)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// Join adds a JOIN clause to the query.
|
||||
func (b SelectBuilder) Join(join string, rest ...interface{}) SelectBuilder {
|
||||
return b.JoinClause("JOIN "+join, rest...)
|
||||
}
|
||||
|
||||
// LeftJoin adds a LEFT JOIN clause to the query.
|
||||
func (b SelectBuilder) LeftJoin(join string, rest ...interface{}) SelectBuilder {
|
||||
return b.JoinClause("LEFT JOIN "+join, rest...)
|
||||
}
|
||||
|
||||
// RightJoin adds a RIGHT JOIN clause to the query.
|
||||
func (b SelectBuilder) RightJoin(join string, rest ...interface{}) SelectBuilder {
|
||||
return b.JoinClause("RIGHT JOIN "+join, rest...)
|
||||
}
|
||||
|
||||
// InnerJoin adds a INNER JOIN clause to the query.
|
||||
func (b SelectBuilder) InnerJoin(join string, rest ...interface{}) SelectBuilder {
|
||||
return b.JoinClause("INNER JOIN "+join, rest...)
|
||||
}
|
||||
|
||||
// CrossJoin adds a CROSS JOIN clause to the query.
|
||||
func (b SelectBuilder) CrossJoin(join string, rest ...interface{}) SelectBuilder {
|
||||
return b.JoinClause("CROSS JOIN "+join, rest...)
|
||||
}
|
||||
|
||||
// Where adds an expression to the WHERE clause of the query.
|
||||
//
|
||||
// Expressions are ANDed together in the generated SQL.
|
||||
//
|
||||
// Where accepts several types for its pred argument:
|
||||
//
|
||||
// nil OR "" - ignored.
|
||||
//
|
||||
// string - SQL expression.
|
||||
// If the expression has SQL placeholders then a set of arguments must be passed
|
||||
// as well, one for each placeholder.
|
||||
//
|
||||
// map[string]interface{} OR Eq - map of SQL expressions to values. Each key is
|
||||
// transformed into an expression like "<key> = ?", with the corresponding value
|
||||
// bound to the placeholder. If the value is nil, the expression will be "<key>
|
||||
// IS NULL". If the value is an array or slice, the expression will be "<key> IN
|
||||
// (?,?,...)", with one placeholder for each item in the value. These expressions
|
||||
// are ANDed together.
|
||||
//
|
||||
// Where will panic if pred isn't any of the above types.
|
||||
func (b SelectBuilder) Where(pred interface{}, args ...interface{}) SelectBuilder {
|
||||
if pred == nil || pred == "" {
|
||||
return b
|
||||
}
|
||||
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// GroupBy adds GROUP BY expressions to the query.
|
||||
func (b SelectBuilder) GroupBy(groupBys ...string) SelectBuilder {
|
||||
return builder.Extend(b, "GroupBys", groupBys).(SelectBuilder)
|
||||
}
|
||||
|
||||
// Having adds an expression to the HAVING clause of the query.
|
||||
//
|
||||
// See Where.
|
||||
func (b SelectBuilder) Having(pred interface{}, rest ...interface{}) SelectBuilder {
|
||||
return builder.Append(b, "HavingParts", newWherePart(pred, rest...)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// OrderByClause adds ORDER BY clause to the query.
|
||||
func (b SelectBuilder) OrderByClause(pred interface{}, args ...interface{}) SelectBuilder {
|
||||
return builder.Append(b, "OrderByParts", newPart(pred, args...)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// OrderBy adds ORDER BY expressions to the query.
|
||||
func (b SelectBuilder) OrderBy(orderBys ...string) SelectBuilder {
|
||||
for _, orderBy := range orderBys {
|
||||
b = b.OrderByClause(orderBy)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Limit sets a LIMIT clause on the query.
|
||||
func (b SelectBuilder) Limit(limit uint64) SelectBuilder {
|
||||
return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// Limit ALL allows to access all records with limit
|
||||
func (b SelectBuilder) RemoveLimit() SelectBuilder {
|
||||
return builder.Delete(b, "Limit").(SelectBuilder)
|
||||
}
|
||||
|
||||
// Offset sets a OFFSET clause on the query.
|
||||
func (b SelectBuilder) Offset(offset uint64) SelectBuilder {
|
||||
return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(SelectBuilder)
|
||||
}
|
||||
|
||||
// RemoveOffset removes OFFSET clause.
|
||||
func (b SelectBuilder) RemoveOffset() SelectBuilder {
|
||||
return builder.Delete(b, "Offset").(SelectBuilder)
|
||||
}
|
||||
|
||||
// Suffix adds an expression to the end of the query
|
||||
func (b SelectBuilder) Suffix(sql string, args ...interface{}) SelectBuilder {
|
||||
return b.SuffixExpr(Expr(sql, args...))
|
||||
}
|
||||
|
||||
// SuffixExpr adds an expression to the end of the query
|
||||
func (b SelectBuilder) SuffixExpr(expr Sqlizer) SelectBuilder {
|
||||
return builder.Append(b, "Suffixes", expr).(SelectBuilder)
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// +build go1.8
|
||||
|
||||
package squirrel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
func (d *selectData) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
ctxRunner, ok := d.RunWith.(ExecerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
return ExecContextWith(ctx, ctxRunner, d)
|
||||
}
|
||||
|
||||
func (d *selectData) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
ctxRunner, ok := d.RunWith.(QueryerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
return QueryContextWith(ctx, ctxRunner, d)
|
||||
}
|
||||
|
||||
func (d *selectData) QueryRowContext(ctx context.Context) RowScanner {
|
||||
if d.RunWith == nil {
|
||||
return &Row{err: RunnerNotSet}
|
||||
}
|
||||
queryRower, ok := d.RunWith.(QueryRowerContext)
|
||||
if !ok {
|
||||
if _, ok := d.RunWith.(QueryerContext); !ok {
|
||||
return &Row{err: RunnerNotQueryRunner}
|
||||
}
|
||||
return &Row{err: NoContextSupport}
|
||||
}
|
||||
return QueryRowContextWith(ctx, queryRower, d)
|
||||
}
|
||||
|
||||
// ExecContext builds and ExecContexts the query with the Runner set by RunWith.
|
||||
func (b SelectBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||
data := builder.GetStruct(b).(selectData)
|
||||
return data.ExecContext(ctx)
|
||||
}
|
||||
|
||||
// QueryContext builds and QueryContexts the query with the Runner set by RunWith.
|
||||
func (b SelectBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||
data := builder.GetStruct(b).(selectData)
|
||||
return data.QueryContext(ctx)
|
||||
}
|
||||
|
||||
// QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith.
|
||||
func (b SelectBuilder) QueryRowContext(ctx context.Context) RowScanner {
|
||||
data := builder.GetStruct(b).(selectData)
|
||||
return data.QueryRowContext(ctx)
|
||||
}
|
||||
|
||||
// ScanContext is a shortcut for QueryRowContext().Scan.
|
||||
func (b SelectBuilder) ScanContext(ctx context.Context, dest ...interface{}) error {
|
||||
return b.QueryRowContext(ctx).Scan(dest...)
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
// Package squirrel provides a fluent SQL generator.
|
||||
//
|
||||
// See https://github.com/Masterminds/squirrel for examples.
|
||||
package squirrel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
// Sqlizer is the interface that wraps the ToSql method.
|
||||
//
|
||||
// ToSql returns a SQL representation of the Sqlizer, along with a slice of args
|
||||
// as passed to e.g. database/sql.Exec. It can also return an error.
|
||||
type Sqlizer interface {
|
||||
ToSql() (string, []interface{}, error)
|
||||
}
|
||||
|
||||
// rawSqlizer is expected to do what Sqlizer does, but without finalizing placeholders.
|
||||
// This is useful for nested queries.
|
||||
type rawSqlizer interface {
|
||||
toSqlRaw() (string, []interface{}, error)
|
||||
}
|
||||
|
||||
// Execer is the interface that wraps the Exec method.
|
||||
//
|
||||
// Exec executes the given query as implemented by database/sql.Exec.
|
||||
type Execer interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
}
|
||||
|
||||
// Queryer is the interface that wraps the Query method.
|
||||
//
|
||||
// Query executes the given query as implemented by database/sql.Query.
|
||||
type Queryer interface {
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
// QueryRower is the interface that wraps the QueryRow method.
|
||||
//
|
||||
// QueryRow executes the given query as implemented by database/sql.QueryRow.
|
||||
type QueryRower interface {
|
||||
QueryRow(query string, args ...interface{}) RowScanner
|
||||
}
|
||||
|
||||
// BaseRunner groups the Execer and Queryer interfaces.
|
||||
type BaseRunner interface {
|
||||
Execer
|
||||
Queryer
|
||||
}
|
||||
|
||||
// Runner groups the Execer, Queryer, and QueryRower interfaces.
|
||||
type Runner interface {
|
||||
Execer
|
||||
Queryer
|
||||
QueryRower
|
||||
}
|
||||
|
||||
// WrapStdSql wraps a type implementing the standard SQL interface with methods that
|
||||
// squirrel expects.
|
||||
func WrapStdSql(stdSql StdSql) Runner {
|
||||
return &stdsqlRunner{stdSql}
|
||||
}
|
||||
|
||||
// StdSql encompasses the standard methods of the *sql.DB type, and other types that
|
||||
// wrap these methods.
|
||||
type StdSql interface {
|
||||
Query(string, ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(string, ...interface{}) *sql.Row
|
||||
Exec(string, ...interface{}) (sql.Result, error)
|
||||
}
|
||||
|
||||
type stdsqlRunner struct {
|
||||
StdSql
|
||||
}
|
||||
|
||||
func (r *stdsqlRunner) QueryRow(query string, args ...interface{}) RowScanner {
|
||||
return r.StdSql.QueryRow(query, args...)
|
||||
}
|
||||
|
||||
func setRunWith(b interface{}, runner BaseRunner) interface{} {
|
||||
switch r := runner.(type) {
|
||||
case StdSqlCtx:
|
||||
runner = WrapStdSqlCtx(r)
|
||||
case StdSql:
|
||||
runner = WrapStdSql(r)
|
||||
}
|
||||
return builder.Set(b, "RunWith", runner)
|
||||
}
|
||||
|
||||
// RunnerNotSet is returned by methods that need a Runner if it isn't set.
|
||||
var RunnerNotSet = fmt.Errorf("cannot run; no Runner set (RunWith)")
|
||||
|
||||
// RunnerNotQueryRunner is returned by QueryRow if the RunWith value doesn't implement QueryRower.
|
||||
var RunnerNotQueryRunner = fmt.Errorf("cannot QueryRow; Runner is not a QueryRower")
|
||||
|
||||
// ExecWith Execs the SQL returned by s with db.
|
||||
func ExecWith(db Execer, s Sqlizer) (res sql.Result, err error) {
|
||||
query, args, err := s.ToSql()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return db.Exec(query, args...)
|
||||
}
|
||||
|
||||
// QueryWith Querys the SQL returned by s with db.
|
||||
func QueryWith(db Queryer, s Sqlizer) (rows *sql.Rows, err error) {
|
||||
query, args, err := s.ToSql()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return db.Query(query, args...)
|
||||
}
|
||||
|
||||
// QueryRowWith QueryRows the SQL returned by s with db.
|
||||
func QueryRowWith(db QueryRower, s Sqlizer) RowScanner {
|
||||
query, args, err := s.ToSql()
|
||||
return &Row{RowScanner: db.QueryRow(query, args...), err: err}
|
||||
}
|
||||
|
||||
// DebugSqlizer calls ToSql on s and shows the approximate SQL to be executed
|
||||
//
|
||||
// If ToSql returns an error, the result of this method will look like:
|
||||
// "[ToSql error: %s]" or "[DebugSqlizer error: %s]"
|
||||
//
|
||||
// IMPORTANT: As its name suggests, this function should only be used for
|
||||
// debugging. While the string result *might* be valid SQL, this function does
|
||||
// not try very hard to ensure it. Additionally, executing the output of this
|
||||
// function with any untrusted user input is certainly insecure.
|
||||
func DebugSqlizer(s Sqlizer) string {
|
||||
sql, args, err := s.ToSql()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("[ToSql error: %s]", err)
|
||||
}
|
||||
|
||||
var placeholder string
|
||||
downCast, ok := s.(placeholderDebugger)
|
||||
if !ok {
|
||||
placeholder = "?"
|
||||
} else {
|
||||
placeholder = downCast.debugPlaceholder()
|
||||
}
|
||||
// TODO: dedupe this with placeholder.go
|
||||
buf := &bytes.Buffer{}
|
||||
i := 0
|
||||
for {
|
||||
p := strings.Index(sql, placeholder)
|
||||
if p == -1 {
|
||||
break
|
||||
}
|
||||
if len(sql[p:]) > 1 && sql[p:p+2] == "??" { // escape ?? => ?
|
||||
buf.WriteString(sql[:p])
|
||||
buf.WriteString("?")
|
||||
if len(sql[p:]) == 1 {
|
||||
break
|
||||
}
|
||||
sql = sql[p+2:]
|
||||
} else {
|
||||
if i+1 > len(args) {
|
||||
return fmt.Sprintf(
|
||||
"[DebugSqlizer error: too many placeholders in %#v for %d args]",
|
||||
sql, len(args))
|
||||
}
|
||||
buf.WriteString(sql[:p])
|
||||
fmt.Fprintf(buf, "'%v'", args[i])
|
||||
// advance our sql string "cursor" beyond the arg we placed
|
||||
sql = sql[p+1:]
|
||||
i++
|
||||
}
|
||||
}
|
||||
if i < len(args) {
|
||||
return fmt.Sprintf(
|
||||
"[DebugSqlizer error: not enough placeholders in %#v for %d args]",
|
||||
sql, len(args))
|
||||
}
|
||||
// "append" any remaning sql that won't need interpolating
|
||||
buf.WriteString(sql)
|
||||
return buf.String()
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// +build go1.8
|
||||
|
||||
package squirrel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// NoContextSupport is returned if a db doesn't support Context.
|
||||
var NoContextSupport = errors.New("DB does not support Context")
|
||||
|
||||
// ExecerContext is the interface that wraps the ExecContext method.
|
||||
//
|
||||
// Exec executes the given query as implemented by database/sql.ExecContext.
|
||||
type ExecerContext interface {
|
||||
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||
}
|
||||
|
||||
// QueryerContext is the interface that wraps the QueryContext method.
|
||||
//
|
||||
// QueryContext executes the given query as implemented by database/sql.QueryContext.
|
||||
type QueryerContext interface {
|
||||
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
// QueryRowerContext is the interface that wraps the QueryRowContext method.
|
||||
//
|
||||
// QueryRowContext executes the given query as implemented by database/sql.QueryRowContext.
|
||||
type QueryRowerContext interface {
|
||||
QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner
|
||||
}
|
||||
|
||||
// RunnerContext groups the Runner interface, along with the Context versions of each of
|
||||
// its methods
|
||||
type RunnerContext interface {
|
||||
Runner
|
||||
QueryerContext
|
||||
QueryRowerContext
|
||||
ExecerContext
|
||||
}
|
||||
|
||||
// WrapStdSqlCtx wraps a type implementing the standard SQL interface plus the context
|
||||
// versions of the methods with methods that squirrel expects.
|
||||
func WrapStdSqlCtx(stdSqlCtx StdSqlCtx) RunnerContext {
|
||||
return &stdsqlCtxRunner{stdSqlCtx}
|
||||
}
|
||||
|
||||
// StdSqlCtx encompasses the standard methods of the *sql.DB type, along with the Context
|
||||
// versions of those methods, and other types that wrap these methods.
|
||||
type StdSqlCtx interface {
|
||||
StdSql
|
||||
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
||||
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||
}
|
||||
|
||||
type stdsqlCtxRunner struct {
|
||||
StdSqlCtx
|
||||
}
|
||||
|
||||
func (r *stdsqlCtxRunner) QueryRow(query string, args ...interface{}) RowScanner {
|
||||
return r.StdSqlCtx.QueryRow(query, args...)
|
||||
}
|
||||
|
||||
func (r *stdsqlCtxRunner) QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner {
|
||||
return r.StdSqlCtx.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
// ExecContextWith ExecContexts the SQL returned by s with db.
|
||||
func ExecContextWith(ctx context.Context, db ExecerContext, s Sqlizer) (res sql.Result, err error) {
|
||||
query, args, err := s.ToSql()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return db.ExecContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
// QueryContextWith QueryContexts the SQL returned by s with db.
|
||||
func QueryContextWith(ctx context.Context, db QueryerContext, s Sqlizer) (rows *sql.Rows, err error) {
|
||||
query, args, err := s.ToSql()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return db.QueryContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
// QueryRowContextWith QueryRowContexts the SQL returned by s with db.
|
||||
func QueryRowContextWith(ctx context.Context, db QueryRowerContext, s Sqlizer) RowScanner {
|
||||
query, args, err := s.ToSql()
|
||||
return &Row{RowScanner: db.QueryRowContext(ctx, query, args...), err: err}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package squirrel
|
||||
|
||||
import "github.com/lann/builder"
|
||||
|
||||
// StatementBuilderType is the type of StatementBuilder.
|
||||
type StatementBuilderType builder.Builder
|
||||
|
||||
// Select returns a SelectBuilder for this StatementBuilderType.
|
||||
func (b StatementBuilderType) Select(columns ...string) SelectBuilder {
|
||||
return SelectBuilder(b).Columns(columns...)
|
||||
}
|
||||
|
||||
// Insert returns a InsertBuilder for this StatementBuilderType.
|
||||
func (b StatementBuilderType) Insert(into string) InsertBuilder {
|
||||
return InsertBuilder(b).Into(into)
|
||||
}
|
||||
|
||||
// Replace returns a InsertBuilder for this StatementBuilderType with the
|
||||
// statement keyword set to "REPLACE".
|
||||
func (b StatementBuilderType) Replace(into string) InsertBuilder {
|
||||
return InsertBuilder(b).statementKeyword("REPLACE").Into(into)
|
||||
}
|
||||
|
||||
// Update returns a UpdateBuilder for this StatementBuilderType.
|
||||
func (b StatementBuilderType) Update(table string) UpdateBuilder {
|
||||
return UpdateBuilder(b).Table(table)
|
||||
}
|
||||
|
||||
// Delete returns a DeleteBuilder for this StatementBuilderType.
|
||||
func (b StatementBuilderType) Delete(from string) DeleteBuilder {
|
||||
return DeleteBuilder(b).From(from)
|
||||
}
|
||||
|
||||
// PlaceholderFormat sets the PlaceholderFormat field for any child builders.
|
||||
func (b StatementBuilderType) PlaceholderFormat(f PlaceholderFormat) StatementBuilderType {
|
||||
return builder.Set(b, "PlaceholderFormat", f).(StatementBuilderType)
|
||||
}
|
||||
|
||||
// RunWith sets the RunWith field for any child builders.
|
||||
func (b StatementBuilderType) RunWith(runner BaseRunner) StatementBuilderType {
|
||||
return setRunWith(b, runner).(StatementBuilderType)
|
||||
}
|
||||
|
||||
// Where adds WHERE expressions to the query.
|
||||
//
|
||||
// See SelectBuilder.Where for more information.
|
||||
func (b StatementBuilderType) Where(pred interface{}, args ...interface{}) StatementBuilderType {
|
||||
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(StatementBuilderType)
|
||||
}
|
||||
|
||||
// StatementBuilder is a parent builder for other builders, e.g. SelectBuilder.
|
||||
var StatementBuilder = StatementBuilderType(builder.EmptyBuilder).PlaceholderFormat(Question)
|
||||
|
||||
// Select returns a new SelectBuilder, optionally setting some result columns.
|
||||
//
|
||||
// See SelectBuilder.Columns.
|
||||
func Select(columns ...string) SelectBuilder {
|
||||
return StatementBuilder.Select(columns...)
|
||||
}
|
||||
|
||||
// Insert returns a new InsertBuilder with the given table name.
|
||||
//
|
||||
// See InsertBuilder.Into.
|
||||
func Insert(into string) InsertBuilder {
|
||||
return StatementBuilder.Insert(into)
|
||||
}
|
||||
|
||||
// Replace returns a new InsertBuilder with the statement keyword set to
|
||||
// "REPLACE" and with the given table name.
|
||||
//
|
||||
// See InsertBuilder.Into.
|
||||
func Replace(into string) InsertBuilder {
|
||||
return StatementBuilder.Replace(into)
|
||||
}
|
||||
|
||||
// Update returns a new UpdateBuilder with the given table name.
|
||||
//
|
||||
// See UpdateBuilder.Table.
|
||||
func Update(table string) UpdateBuilder {
|
||||
return StatementBuilder.Update(table)
|
||||
}
|
||||
|
||||
// Delete returns a new DeleteBuilder with the given table name.
|
||||
//
|
||||
// See DeleteBuilder.Table.
|
||||
func Delete(from string) DeleteBuilder {
|
||||
return StatementBuilder.Delete(from)
|
||||
}
|
||||
|
||||
// Case returns a new CaseBuilder
|
||||
// "what" represents case value
|
||||
func Case(what ...interface{}) CaseBuilder {
|
||||
b := CaseBuilder(builder.EmptyBuilder)
|
||||
|
||||
switch len(what) {
|
||||
case 0:
|
||||
case 1:
|
||||
b = b.what(what[0])
|
||||
default:
|
||||
b = b.what(newPart(what[0], what[1:]...))
|
||||
|
||||
}
|
||||
return b
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Prepareer is the interface that wraps the Prepare method.
|
||||
//
|
||||
// Prepare executes the given query as implemented by database/sql.Prepare.
|
||||
type Preparer interface {
|
||||
Prepare(query string) (*sql.Stmt, error)
|
||||
}
|
||||
|
||||
// DBProxy groups the Execer, Queryer, QueryRower, and Preparer interfaces.
|
||||
type DBProxy interface {
|
||||
Execer
|
||||
Queryer
|
||||
QueryRower
|
||||
Preparer
|
||||
}
|
||||
|
||||
// NOTE: NewStmtCache is defined in stmtcacher_ctx.go (Go >= 1.8) or stmtcacher_noctx.go (Go < 1.8).
|
||||
|
||||
// StmtCache wraps and delegates down to a Preparer type
|
||||
//
|
||||
// It also automatically prepares all statements sent to the underlying Preparer calls
|
||||
// for Exec, Query and QueryRow and caches the returns *sql.Stmt using the provided
|
||||
// query as the key. So that it can be automatically re-used.
|
||||
type StmtCache struct {
|
||||
prep Preparer
|
||||
cache map[string]*sql.Stmt
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// Prepare delegates down to the underlying Preparer and caches the result
|
||||
// using the provided query as a key
|
||||
func (sc *StmtCache) Prepare(query string) (*sql.Stmt, error) {
|
||||
sc.mu.Lock()
|
||||
defer sc.mu.Unlock()
|
||||
|
||||
stmt, ok := sc.cache[query]
|
||||
if ok {
|
||||
return stmt, nil
|
||||
}
|
||||
stmt, err := sc.prep.Prepare(query)
|
||||
if err == nil {
|
||||
sc.cache[query] = stmt
|
||||
}
|
||||
return stmt, err
|
||||
}
|
||||
|
||||
// Exec delegates down to the underlying Preparer using a prepared statement
|
||||
func (sc *StmtCache) Exec(query string, args ...interface{}) (res sql.Result, err error) {
|
||||
stmt, err := sc.Prepare(query)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return stmt.Exec(args...)
|
||||
}
|
||||
|
||||
// Query delegates down to the underlying Preparer using a prepared statement
|
||||
func (sc *StmtCache) Query(query string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
stmt, err := sc.Prepare(query)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return stmt.Query(args...)
|
||||
}
|
||||
|
||||
// QueryRow delegates down to the underlying Preparer using a prepared statement
|
||||
func (sc *StmtCache) QueryRow(query string, args ...interface{}) RowScanner {
|
||||
stmt, err := sc.Prepare(query)
|
||||
if err != nil {
|
||||
return &Row{err: err}
|
||||
}
|
||||
return stmt.QueryRow(args...)
|
||||
}
|
||||
|
||||
// Clear removes and closes all the currently cached prepared statements
|
||||
func (sc *StmtCache) Clear() (err error) {
|
||||
sc.mu.Lock()
|
||||
defer sc.mu.Unlock()
|
||||
|
||||
for key, stmt := range sc.cache {
|
||||
delete(sc.cache, key)
|
||||
|
||||
if stmt == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if cerr := stmt.Close(); cerr != nil {
|
||||
err = cerr
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("one or more Stmt.Close failed; last error: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type DBProxyBeginner interface {
|
||||
DBProxy
|
||||
Begin() (*sql.Tx, error)
|
||||
}
|
||||
|
||||
type stmtCacheProxy struct {
|
||||
DBProxy
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewStmtCacheProxy(db *sql.DB) DBProxyBeginner {
|
||||
return &stmtCacheProxy{DBProxy: NewStmtCache(db), db: db}
|
||||
}
|
||||
|
||||
func (sp *stmtCacheProxy) Begin() (*sql.Tx, error) {
|
||||
return sp.db.Begin()
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// +build go1.8
|
||||
|
||||
package squirrel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// PrepareerContext is the interface that wraps the Prepare and PrepareContext methods.
|
||||
//
|
||||
// Prepare executes the given query as implemented by database/sql.Prepare.
|
||||
// PrepareContext executes the given query as implemented by database/sql.PrepareContext.
|
||||
type PreparerContext interface {
|
||||
Preparer
|
||||
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
|
||||
}
|
||||
|
||||
// DBProxyContext groups the Execer, Queryer, QueryRower and PreparerContext interfaces.
|
||||
type DBProxyContext interface {
|
||||
Execer
|
||||
Queryer
|
||||
QueryRower
|
||||
PreparerContext
|
||||
}
|
||||
|
||||
// NewStmtCache returns a *StmtCache wrapping a PreparerContext that caches Prepared Stmts.
|
||||
//
|
||||
// Stmts are cached based on the string value of their queries.
|
||||
func NewStmtCache(prep PreparerContext) *StmtCache {
|
||||
return &StmtCache{prep: prep, cache: make(map[string]*sql.Stmt)}
|
||||
}
|
||||
|
||||
// NewStmtCacher is deprecated
|
||||
//
|
||||
// Use NewStmtCache instead
|
||||
func NewStmtCacher(prep PreparerContext) DBProxyContext {
|
||||
return NewStmtCache(prep)
|
||||
}
|
||||
|
||||
// PrepareContext delegates down to the underlying PreparerContext and caches the result
|
||||
// using the provided query as a key
|
||||
func (sc *StmtCache) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
|
||||
ctxPrep, ok := sc.prep.(PreparerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
sc.mu.Lock()
|
||||
defer sc.mu.Unlock()
|
||||
stmt, ok := sc.cache[query]
|
||||
if ok {
|
||||
return stmt, nil
|
||||
}
|
||||
stmt, err := ctxPrep.PrepareContext(ctx, query)
|
||||
if err == nil {
|
||||
sc.cache[query] = stmt
|
||||
}
|
||||
return stmt, err
|
||||
}
|
||||
|
||||
// ExecContext delegates down to the underlying PreparerContext using a prepared statement
|
||||
func (sc *StmtCache) ExecContext(ctx context.Context, query string, args ...interface{}) (res sql.Result, err error) {
|
||||
stmt, err := sc.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return stmt.ExecContext(ctx, args...)
|
||||
}
|
||||
|
||||
// QueryContext delegates down to the underlying PreparerContext using a prepared statement
|
||||
func (sc *StmtCache) QueryContext(ctx context.Context, query string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
stmt, err := sc.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return stmt.QueryContext(ctx, args...)
|
||||
}
|
||||
|
||||
// QueryRowContext delegates down to the underlying PreparerContext using a prepared statement
|
||||
func (sc *StmtCache) QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner {
|
||||
stmt, err := sc.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
return &Row{err: err}
|
||||
}
|
||||
return stmt.QueryRowContext(ctx, args...)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// +build !go1.8
|
||||
|
||||
package squirrel
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// NewStmtCacher returns a DBProxy wrapping prep that caches Prepared Stmts.
|
||||
//
|
||||
// Stmts are cached based on the string value of their queries.
|
||||
func NewStmtCache(prep Preparer) *StmtCache {
|
||||
return &StmtCacher{prep: prep, cache: make(map[string]*sql.Stmt)}
|
||||
}
|
||||
|
||||
// NewStmtCacher is deprecated
|
||||
//
|
||||
// Use NewStmtCache instead
|
||||
func NewStmtCacher(prep Preparer) DBProxy {
|
||||
return NewStmtCache(prep)
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
type updateData struct {
|
||||
PlaceholderFormat PlaceholderFormat
|
||||
RunWith BaseRunner
|
||||
Prefixes []Sqlizer
|
||||
Table string
|
||||
SetClauses []setClause
|
||||
From Sqlizer
|
||||
WhereParts []Sqlizer
|
||||
OrderBys []string
|
||||
Limit string
|
||||
Offset string
|
||||
Suffixes []Sqlizer
|
||||
}
|
||||
|
||||
type setClause struct {
|
||||
column string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (d *updateData) Exec() (sql.Result, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
return ExecWith(d.RunWith, d)
|
||||
}
|
||||
|
||||
func (d *updateData) Query() (*sql.Rows, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
return QueryWith(d.RunWith, d)
|
||||
}
|
||||
|
||||
func (d *updateData) QueryRow() RowScanner {
|
||||
if d.RunWith == nil {
|
||||
return &Row{err: RunnerNotSet}
|
||||
}
|
||||
queryRower, ok := d.RunWith.(QueryRower)
|
||||
if !ok {
|
||||
return &Row{err: RunnerNotQueryRunner}
|
||||
}
|
||||
return QueryRowWith(queryRower, d)
|
||||
}
|
||||
|
||||
func (d *updateData) ToSql() (sqlStr string, args []interface{}, err error) {
|
||||
if len(d.Table) == 0 {
|
||||
err = fmt.Errorf("update statements must specify a table")
|
||||
return
|
||||
}
|
||||
if len(d.SetClauses) == 0 {
|
||||
err = fmt.Errorf("update statements must have at least one Set clause")
|
||||
return
|
||||
}
|
||||
|
||||
sql := &bytes.Buffer{}
|
||||
|
||||
if len(d.Prefixes) > 0 {
|
||||
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sql.WriteString(" ")
|
||||
}
|
||||
|
||||
sql.WriteString("UPDATE ")
|
||||
sql.WriteString(d.Table)
|
||||
|
||||
sql.WriteString(" SET ")
|
||||
setSqls := make([]string, len(d.SetClauses))
|
||||
for i, setClause := range d.SetClauses {
|
||||
var valSql string
|
||||
if vs, ok := setClause.value.(Sqlizer); ok {
|
||||
vsql, vargs, err := vs.ToSql()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if _, ok := vs.(SelectBuilder); ok {
|
||||
valSql = fmt.Sprintf("(%s)", vsql)
|
||||
} else {
|
||||
valSql = vsql
|
||||
}
|
||||
args = append(args, vargs...)
|
||||
} else {
|
||||
valSql = "?"
|
||||
args = append(args, setClause.value)
|
||||
}
|
||||
setSqls[i] = fmt.Sprintf("%s = %s", setClause.column, valSql)
|
||||
}
|
||||
sql.WriteString(strings.Join(setSqls, ", "))
|
||||
|
||||
if d.From != nil {
|
||||
sql.WriteString(" FROM ")
|
||||
args, err = appendToSql([]Sqlizer{d.From}, sql, "", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.WhereParts) > 0 {
|
||||
sql.WriteString(" WHERE ")
|
||||
args, err = appendToSql(d.WhereParts, sql, " AND ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.OrderBys) > 0 {
|
||||
sql.WriteString(" ORDER BY ")
|
||||
sql.WriteString(strings.Join(d.OrderBys, ", "))
|
||||
}
|
||||
|
||||
if len(d.Limit) > 0 {
|
||||
sql.WriteString(" LIMIT ")
|
||||
sql.WriteString(d.Limit)
|
||||
}
|
||||
|
||||
if len(d.Offset) > 0 {
|
||||
sql.WriteString(" OFFSET ")
|
||||
sql.WriteString(d.Offset)
|
||||
}
|
||||
|
||||
if len(d.Suffixes) > 0 {
|
||||
sql.WriteString(" ")
|
||||
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Builder
|
||||
|
||||
// UpdateBuilder builds SQL UPDATE statements.
|
||||
type UpdateBuilder builder.Builder
|
||||
|
||||
func init() {
|
||||
builder.Register(UpdateBuilder{}, updateData{})
|
||||
}
|
||||
|
||||
// Format methods
|
||||
|
||||
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
||||
// query.
|
||||
func (b UpdateBuilder) PlaceholderFormat(f PlaceholderFormat) UpdateBuilder {
|
||||
return builder.Set(b, "PlaceholderFormat", f).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// Runner methods
|
||||
|
||||
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
||||
func (b UpdateBuilder) RunWith(runner BaseRunner) UpdateBuilder {
|
||||
return setRunWith(b, runner).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// Exec builds and Execs the query with the Runner set by RunWith.
|
||||
func (b UpdateBuilder) Exec() (sql.Result, error) {
|
||||
data := builder.GetStruct(b).(updateData)
|
||||
return data.Exec()
|
||||
}
|
||||
|
||||
func (b UpdateBuilder) Query() (*sql.Rows, error) {
|
||||
data := builder.GetStruct(b).(updateData)
|
||||
return data.Query()
|
||||
}
|
||||
|
||||
func (b UpdateBuilder) QueryRow() RowScanner {
|
||||
data := builder.GetStruct(b).(updateData)
|
||||
return data.QueryRow()
|
||||
}
|
||||
|
||||
func (b UpdateBuilder) Scan(dest ...interface{}) error {
|
||||
return b.QueryRow().Scan(dest...)
|
||||
}
|
||||
|
||||
// SQL methods
|
||||
|
||||
// ToSql builds the query into a SQL string and bound args.
|
||||
func (b UpdateBuilder) ToSql() (string, []interface{}, error) {
|
||||
data := builder.GetStruct(b).(updateData)
|
||||
return data.ToSql()
|
||||
}
|
||||
|
||||
// MustSql builds the query into a SQL string and bound args.
|
||||
// It panics if there are any errors.
|
||||
func (b UpdateBuilder) MustSql() (string, []interface{}) {
|
||||
sql, args, err := b.ToSql()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sql, args
|
||||
}
|
||||
|
||||
// Prefix adds an expression to the beginning of the query
|
||||
func (b UpdateBuilder) Prefix(sql string, args ...interface{}) UpdateBuilder {
|
||||
return b.PrefixExpr(Expr(sql, args...))
|
||||
}
|
||||
|
||||
// PrefixExpr adds an expression to the very beginning of the query
|
||||
func (b UpdateBuilder) PrefixExpr(expr Sqlizer) UpdateBuilder {
|
||||
return builder.Append(b, "Prefixes", expr).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// Table sets the table to be updated.
|
||||
func (b UpdateBuilder) Table(table string) UpdateBuilder {
|
||||
return builder.Set(b, "Table", table).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// Set adds SET clauses to the query.
|
||||
func (b UpdateBuilder) Set(column string, value interface{}) UpdateBuilder {
|
||||
return builder.Append(b, "SetClauses", setClause{column: column, value: value}).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// SetMap is a convenience method which calls .Set for each key/value pair in clauses.
|
||||
func (b UpdateBuilder) SetMap(clauses map[string]interface{}) UpdateBuilder {
|
||||
keys := make([]string, len(clauses))
|
||||
i := 0
|
||||
for key := range clauses {
|
||||
keys[i] = key
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
val, _ := clauses[key]
|
||||
b = b.Set(key, val)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// From adds FROM clause to the query
|
||||
// FROM is valid construct in postgresql only.
|
||||
func (b UpdateBuilder) From(from string) UpdateBuilder {
|
||||
return builder.Set(b, "From", newPart(from)).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// FromSelect sets a subquery into the FROM clause of the query.
|
||||
func (b UpdateBuilder) FromSelect(from SelectBuilder, alias string) UpdateBuilder {
|
||||
// Prevent misnumbered parameters in nested selects (#183).
|
||||
from = from.PlaceholderFormat(Question)
|
||||
return builder.Set(b, "From", Alias(from, alias)).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// Where adds WHERE expressions to the query.
|
||||
//
|
||||
// See SelectBuilder.Where for more information.
|
||||
func (b UpdateBuilder) Where(pred interface{}, args ...interface{}) UpdateBuilder {
|
||||
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// OrderBy adds ORDER BY expressions to the query.
|
||||
func (b UpdateBuilder) OrderBy(orderBys ...string) UpdateBuilder {
|
||||
return builder.Extend(b, "OrderBys", orderBys).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// Limit sets a LIMIT clause on the query.
|
||||
func (b UpdateBuilder) Limit(limit uint64) UpdateBuilder {
|
||||
return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// Offset sets a OFFSET clause on the query.
|
||||
func (b UpdateBuilder) Offset(offset uint64) UpdateBuilder {
|
||||
return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(UpdateBuilder)
|
||||
}
|
||||
|
||||
// Suffix adds an expression to the end of the query
|
||||
func (b UpdateBuilder) Suffix(sql string, args ...interface{}) UpdateBuilder {
|
||||
return b.SuffixExpr(Expr(sql, args...))
|
||||
}
|
||||
|
||||
// SuffixExpr adds an expression to the end of the query
|
||||
func (b UpdateBuilder) SuffixExpr(expr Sqlizer) UpdateBuilder {
|
||||
return builder.Append(b, "Suffixes", expr).(UpdateBuilder)
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// +build go1.8
|
||||
|
||||
package squirrel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/lann/builder"
|
||||
)
|
||||
|
||||
func (d *updateData) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
ctxRunner, ok := d.RunWith.(ExecerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
return ExecContextWith(ctx, ctxRunner, d)
|
||||
}
|
||||
|
||||
func (d *updateData) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||
if d.RunWith == nil {
|
||||
return nil, RunnerNotSet
|
||||
}
|
||||
ctxRunner, ok := d.RunWith.(QueryerContext)
|
||||
if !ok {
|
||||
return nil, NoContextSupport
|
||||
}
|
||||
return QueryContextWith(ctx, ctxRunner, d)
|
||||
}
|
||||
|
||||
func (d *updateData) QueryRowContext(ctx context.Context) RowScanner {
|
||||
if d.RunWith == nil {
|
||||
return &Row{err: RunnerNotSet}
|
||||
}
|
||||
queryRower, ok := d.RunWith.(QueryRowerContext)
|
||||
if !ok {
|
||||
if _, ok := d.RunWith.(QueryerContext); !ok {
|
||||
return &Row{err: RunnerNotQueryRunner}
|
||||
}
|
||||
return &Row{err: NoContextSupport}
|
||||
}
|
||||
return QueryRowContextWith(ctx, queryRower, d)
|
||||
}
|
||||
|
||||
// ExecContext builds and ExecContexts the query with the Runner set by RunWith.
|
||||
func (b UpdateBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
|
||||
data := builder.GetStruct(b).(updateData)
|
||||
return data.ExecContext(ctx)
|
||||
}
|
||||
|
||||
// QueryContext builds and QueryContexts the query with the Runner set by RunWith.
|
||||
func (b UpdateBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) {
|
||||
data := builder.GetStruct(b).(updateData)
|
||||
return data.QueryContext(ctx)
|
||||
}
|
||||
|
||||
// QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith.
|
||||
func (b UpdateBuilder) QueryRowContext(ctx context.Context) RowScanner {
|
||||
data := builder.GetStruct(b).(updateData)
|
||||
return data.QueryRowContext(ctx)
|
||||
}
|
||||
|
||||
// ScanContext is a shortcut for QueryRowContext().Scan.
|
||||
func (b UpdateBuilder) ScanContext(ctx context.Context, dest ...interface{}) error {
|
||||
return b.QueryRowContext(ctx).Scan(dest...)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package squirrel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type wherePart part
|
||||
|
||||
func newWherePart(pred interface{}, args ...interface{}) Sqlizer {
|
||||
return &wherePart{pred: pred, args: args}
|
||||
}
|
||||
|
||||
func (p wherePart) ToSql() (sql string, args []interface{}, err error) {
|
||||
switch pred := p.pred.(type) {
|
||||
case nil:
|
||||
// no-op
|
||||
case rawSqlizer:
|
||||
return pred.toSqlRaw()
|
||||
case Sqlizer:
|
||||
return pred.ToSql()
|
||||
case map[string]interface{}:
|
||||
return Eq(pred).ToSql()
|
||||
case string:
|
||||
sql = pred
|
||||
args = p.args
|
||||
default:
|
||||
err = fmt.Errorf("expected string-keyed map or string, not %T", pred)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (C) 2013 Blake Mizerany
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,316 @@
|
|||
// Package quantile computes approximate quantiles over an unbounded data
|
||||
// stream within low memory and CPU bounds.
|
||||
//
|
||||
// A small amount of accuracy is traded to achieve the above properties.
|
||||
//
|
||||
// Multiple streams can be merged before calling Query to generate a single set
|
||||
// of results. This is meaningful when the streams represent the same type of
|
||||
// data. See Merge and Samples.
|
||||
//
|
||||
// For more detailed information about the algorithm used, see:
|
||||
//
|
||||
// Effective Computation of Biased Quantiles over Data Streams
|
||||
//
|
||||
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
|
||||
package quantile
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Sample holds an observed value and meta information for compression. JSON
|
||||
// tags have been added for convenience.
|
||||
type Sample struct {
|
||||
Value float64 `json:",string"`
|
||||
Width float64 `json:",string"`
|
||||
Delta float64 `json:",string"`
|
||||
}
|
||||
|
||||
// Samples represents a slice of samples. It implements sort.Interface.
|
||||
type Samples []Sample
|
||||
|
||||
func (a Samples) Len() int { return len(a) }
|
||||
func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||
func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
type invariant func(s *stream, r float64) float64
|
||||
|
||||
// NewLowBiased returns an initialized Stream for low-biased quantiles
|
||||
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||
// error guarantees can still be given even for the lower ranks of the data
|
||||
// distribution.
|
||||
//
|
||||
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||
// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
|
||||
//
|
||||
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||
// properties.
|
||||
func NewLowBiased(epsilon float64) *Stream {
|
||||
ƒ := func(s *stream, r float64) float64 {
|
||||
return 2 * epsilon * r
|
||||
}
|
||||
return newStream(ƒ)
|
||||
}
|
||||
|
||||
// NewHighBiased returns an initialized Stream for high-biased quantiles
|
||||
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||
// error guarantees can still be given even for the higher ranks of the data
|
||||
// distribution.
|
||||
//
|
||||
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||
// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
|
||||
//
|
||||
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||
// properties.
|
||||
func NewHighBiased(epsilon float64) *Stream {
|
||||
ƒ := func(s *stream, r float64) float64 {
|
||||
return 2 * epsilon * (s.n - r)
|
||||
}
|
||||
return newStream(ƒ)
|
||||
}
|
||||
|
||||
// NewTargeted returns an initialized Stream concerned with a particular set of
|
||||
// quantile values that are supplied a priori. Knowing these a priori reduces
|
||||
// space and computation time. The targets map maps the desired quantiles to
|
||||
// their absolute errors, i.e. the true quantile of a value returned by a query
|
||||
// is guaranteed to be within (Quantile±Epsilon).
|
||||
//
|
||||
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
|
||||
func NewTargeted(targetMap map[float64]float64) *Stream {
|
||||
// Convert map to slice to avoid slow iterations on a map.
|
||||
// ƒ is called on the hot path, so converting the map to a slice
|
||||
// beforehand results in significant CPU savings.
|
||||
targets := targetMapToSlice(targetMap)
|
||||
|
||||
ƒ := func(s *stream, r float64) float64 {
|
||||
var m = math.MaxFloat64
|
||||
var f float64
|
||||
for _, t := range targets {
|
||||
if t.quantile*s.n <= r {
|
||||
f = (2 * t.epsilon * r) / t.quantile
|
||||
} else {
|
||||
f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
|
||||
}
|
||||
if f < m {
|
||||
m = f
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
return newStream(ƒ)
|
||||
}
|
||||
|
||||
type target struct {
|
||||
quantile float64
|
||||
epsilon float64
|
||||
}
|
||||
|
||||
func targetMapToSlice(targetMap map[float64]float64) []target {
|
||||
targets := make([]target, 0, len(targetMap))
|
||||
|
||||
for quantile, epsilon := range targetMap {
|
||||
t := target{
|
||||
quantile: quantile,
|
||||
epsilon: epsilon,
|
||||
}
|
||||
targets = append(targets, t)
|
||||
}
|
||||
|
||||
return targets
|
||||
}
|
||||
|
||||
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
|
||||
// design. Take care when using across multiple goroutines.
|
||||
type Stream struct {
|
||||
*stream
|
||||
b Samples
|
||||
sorted bool
|
||||
}
|
||||
|
||||
func newStream(ƒ invariant) *Stream {
|
||||
x := &stream{ƒ: ƒ}
|
||||
return &Stream{x, make(Samples, 0, 500), true}
|
||||
}
|
||||
|
||||
// Insert inserts v into the stream.
|
||||
func (s *Stream) Insert(v float64) {
|
||||
s.insert(Sample{Value: v, Width: 1})
|
||||
}
|
||||
|
||||
func (s *Stream) insert(sample Sample) {
|
||||
s.b = append(s.b, sample)
|
||||
s.sorted = false
|
||||
if len(s.b) == cap(s.b) {
|
||||
s.flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Query returns the computed qth percentiles value. If s was created with
|
||||
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
|
||||
// will return an unspecified result.
|
||||
func (s *Stream) Query(q float64) float64 {
|
||||
if !s.flushed() {
|
||||
// Fast path when there hasn't been enough data for a flush;
|
||||
// this also yields better accuracy for small sets of data.
|
||||
l := len(s.b)
|
||||
if l == 0 {
|
||||
return 0
|
||||
}
|
||||
i := int(math.Ceil(float64(l) * q))
|
||||
if i > 0 {
|
||||
i -= 1
|
||||
}
|
||||
s.maybeSort()
|
||||
return s.b[i].Value
|
||||
}
|
||||
s.flush()
|
||||
return s.stream.query(q)
|
||||
}
|
||||
|
||||
// Merge merges samples into the underlying streams samples. This is handy when
|
||||
// merging multiple streams from separate threads, database shards, etc.
|
||||
//
|
||||
// ATTENTION: This method is broken and does not yield correct results. The
|
||||
// underlying algorithm is not capable of merging streams correctly.
|
||||
func (s *Stream) Merge(samples Samples) {
|
||||
sort.Sort(samples)
|
||||
s.stream.merge(samples)
|
||||
}
|
||||
|
||||
// Reset reinitializes and clears the list reusing the samples buffer memory.
|
||||
func (s *Stream) Reset() {
|
||||
s.stream.reset()
|
||||
s.b = s.b[:0]
|
||||
}
|
||||
|
||||
// Samples returns stream samples held by s.
|
||||
func (s *Stream) Samples() Samples {
|
||||
if !s.flushed() {
|
||||
return s.b
|
||||
}
|
||||
s.flush()
|
||||
return s.stream.samples()
|
||||
}
|
||||
|
||||
// Count returns the total number of samples observed in the stream
|
||||
// since initialization.
|
||||
func (s *Stream) Count() int {
|
||||
return len(s.b) + s.stream.count()
|
||||
}
|
||||
|
||||
func (s *Stream) flush() {
|
||||
s.maybeSort()
|
||||
s.stream.merge(s.b)
|
||||
s.b = s.b[:0]
|
||||
}
|
||||
|
||||
func (s *Stream) maybeSort() {
|
||||
if !s.sorted {
|
||||
s.sorted = true
|
||||
sort.Sort(s.b)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) flushed() bool {
|
||||
return len(s.stream.l) > 0
|
||||
}
|
||||
|
||||
type stream struct {
|
||||
n float64
|
||||
l []Sample
|
||||
ƒ invariant
|
||||
}
|
||||
|
||||
func (s *stream) reset() {
|
||||
s.l = s.l[:0]
|
||||
s.n = 0
|
||||
}
|
||||
|
||||
func (s *stream) insert(v float64) {
|
||||
s.merge(Samples{{v, 1, 0}})
|
||||
}
|
||||
|
||||
func (s *stream) merge(samples Samples) {
|
||||
// TODO(beorn7): This tries to merge not only individual samples, but
|
||||
// whole summaries. The paper doesn't mention merging summaries at
|
||||
// all. Unittests show that the merging is inaccurate. Find out how to
|
||||
// do merges properly.
|
||||
var r float64
|
||||
i := 0
|
||||
for _, sample := range samples {
|
||||
for ; i < len(s.l); i++ {
|
||||
c := s.l[i]
|
||||
if c.Value > sample.Value {
|
||||
// Insert at position i.
|
||||
s.l = append(s.l, Sample{})
|
||||
copy(s.l[i+1:], s.l[i:])
|
||||
s.l[i] = Sample{
|
||||
sample.Value,
|
||||
sample.Width,
|
||||
math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
|
||||
// TODO(beorn7): How to calculate delta correctly?
|
||||
}
|
||||
i++
|
||||
goto inserted
|
||||
}
|
||||
r += c.Width
|
||||
}
|
||||
s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
|
||||
i++
|
||||
inserted:
|
||||
s.n += sample.Width
|
||||
r += sample.Width
|
||||
}
|
||||
s.compress()
|
||||
}
|
||||
|
||||
func (s *stream) count() int {
|
||||
return int(s.n)
|
||||
}
|
||||
|
||||
func (s *stream) query(q float64) float64 {
|
||||
t := math.Ceil(q * s.n)
|
||||
t += math.Ceil(s.ƒ(s, t) / 2)
|
||||
p := s.l[0]
|
||||
var r float64
|
||||
for _, c := range s.l[1:] {
|
||||
r += p.Width
|
||||
if r+c.Width+c.Delta > t {
|
||||
return p.Value
|
||||
}
|
||||
p = c
|
||||
}
|
||||
return p.Value
|
||||
}
|
||||
|
||||
func (s *stream) compress() {
|
||||
if len(s.l) < 2 {
|
||||
return
|
||||
}
|
||||
x := s.l[len(s.l)-1]
|
||||
xi := len(s.l) - 1
|
||||
r := s.n - 1 - x.Width
|
||||
|
||||
for i := len(s.l) - 2; i >= 0; i-- {
|
||||
c := s.l[i]
|
||||
if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
|
||||
x.Width += c.Width
|
||||
s.l[xi] = x
|
||||
// Remove element at i.
|
||||
copy(s.l[i:], s.l[i+1:])
|
||||
s.l = s.l[:len(s.l)-1]
|
||||
xi -= 1
|
||||
} else {
|
||||
x = c
|
||||
xi = i
|
||||
}
|
||||
r -= c.Width
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stream) samples() Samples {
|
||||
samples := make(Samples, len(s.l))
|
||||
copy(samples, s.l)
|
||||
return samples
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,16 @@
|
|||
### go-simplejson
|
||||
|
||||
a Go package to interact with arbitrary JSON
|
||||
|
||||
[](https://github.com/bitly/go-simplejson/actions)
|
||||
[](https://pkg.go.dev/github.com/bitly/go-simplejson)
|
||||
[](https://github.com/bitly/go-simplejson/releases/latest)
|
||||
|
||||
|
||||
### Importing
|
||||
|
||||
import github.com/bitly/go-simplejson
|
||||
|
||||
### Documentation
|
||||
|
||||
Visit the docs on [Go package discovery & docs](https://pkg.go.dev/github.com/bitly/go-simplejson)
|
|
@ -0,0 +1,458 @@
|
|||
package simplejson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
)
|
||||
|
||||
// returns the current implementation version
|
||||
func Version() string {
|
||||
return "0.5.1"
|
||||
}
|
||||
|
||||
type Json struct {
|
||||
data interface{}
|
||||
}
|
||||
|
||||
// NewJson returns a pointer to a new `Json` object
|
||||
// after unmarshaling `body` bytes
|
||||
func NewJson(body []byte) (*Json, error) {
|
||||
j := new(Json)
|
||||
err := j.UnmarshalJSON(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return j, nil
|
||||
}
|
||||
|
||||
// New returns a pointer to a new, empty `Json` object
|
||||
func New() *Json {
|
||||
return &Json{
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Interface returns the underlying data
|
||||
func (j *Json) Interface() interface{} {
|
||||
return j.data
|
||||
}
|
||||
|
||||
// Encode returns its marshaled data as `[]byte`
|
||||
func (j *Json) Encode() ([]byte, error) {
|
||||
return j.MarshalJSON()
|
||||
}
|
||||
|
||||
// EncodePretty returns its marshaled data as `[]byte` with indentation
|
||||
func (j *Json) EncodePretty() ([]byte, error) {
|
||||
return json.MarshalIndent(&j.data, "", " ")
|
||||
}
|
||||
|
||||
// Implements the json.Marshaler interface.
|
||||
func (j *Json) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&j.data)
|
||||
}
|
||||
|
||||
// Set modifies `Json` map by `key` and `value`
|
||||
// Useful for changing single key/value in a `Json` object easily.
|
||||
func (j *Json) Set(key string, val interface{}) {
|
||||
m, err := j.Map()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m[key] = val
|
||||
}
|
||||
|
||||
// SetPath modifies `Json`, recursively checking/creating map keys for the supplied path,
|
||||
// and then finally writing in the value
|
||||
func (j *Json) SetPath(branch []string, val interface{}) {
|
||||
if len(branch) == 0 {
|
||||
j.data = val
|
||||
return
|
||||
}
|
||||
|
||||
// in order to insert our branch, we need map[string]interface{}
|
||||
if _, ok := (j.data).(map[string]interface{}); !ok {
|
||||
// have to replace with something suitable
|
||||
j.data = make(map[string]interface{})
|
||||
}
|
||||
curr := j.data.(map[string]interface{})
|
||||
|
||||
for i := 0; i < len(branch)-1; i++ {
|
||||
b := branch[i]
|
||||
// key exists?
|
||||
if _, ok := curr[b]; !ok {
|
||||
n := make(map[string]interface{})
|
||||
curr[b] = n
|
||||
curr = n
|
||||
continue
|
||||
}
|
||||
|
||||
// make sure the value is the right sort of thing
|
||||
if _, ok := curr[b].(map[string]interface{}); !ok {
|
||||
// have to replace with something suitable
|
||||
n := make(map[string]interface{})
|
||||
curr[b] = n
|
||||
}
|
||||
|
||||
curr = curr[b].(map[string]interface{})
|
||||
}
|
||||
|
||||
// add remaining k/v
|
||||
curr[branch[len(branch)-1]] = val
|
||||
}
|
||||
|
||||
// Del modifies `Json` map by deleting `key` if it is present.
|
||||
func (j *Json) Del(key string) {
|
||||
m, err := j.Map()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
delete(m, key)
|
||||
}
|
||||
|
||||
// Get returns a pointer to a new `Json` object
|
||||
// for `key` in its `map` representation
|
||||
//
|
||||
// useful for chaining operations (to traverse a nested JSON):
|
||||
//
|
||||
// js.Get("top_level").Get("dict").Get("value").Int()
|
||||
func (j *Json) Get(key string) *Json {
|
||||
m, err := j.Map()
|
||||
if err == nil {
|
||||
if val, ok := m[key]; ok {
|
||||
return &Json{val}
|
||||
}
|
||||
}
|
||||
return &Json{nil}
|
||||
}
|
||||
|
||||
// GetPath searches for the item as specified by the branch
|
||||
// without the need to deep dive using Get()'s.
|
||||
//
|
||||
// js.GetPath("top_level", "dict")
|
||||
func (j *Json) GetPath(branch ...string) *Json {
|
||||
jin := j
|
||||
for _, p := range branch {
|
||||
jin = jin.Get(p)
|
||||
}
|
||||
return jin
|
||||
}
|
||||
|
||||
// GetIndex returns a pointer to a new `Json` object
|
||||
// for `index` in its `array` representation
|
||||
//
|
||||
// this is the analog to Get when accessing elements of
|
||||
// a json array instead of a json object:
|
||||
//
|
||||
// js.Get("top_level").Get("array").GetIndex(1).Get("key").Int()
|
||||
func (j *Json) GetIndex(index int) *Json {
|
||||
a, err := j.Array()
|
||||
if err == nil {
|
||||
if len(a) > index {
|
||||
return &Json{a[index]}
|
||||
}
|
||||
}
|
||||
return &Json{nil}
|
||||
}
|
||||
|
||||
// CheckGet returns a pointer to a new `Json` object and
|
||||
// a `bool` identifying success or failure
|
||||
//
|
||||
// useful for chained operations when success is important:
|
||||
//
|
||||
// if data, ok := js.Get("top_level").CheckGet("inner"); ok {
|
||||
// log.Println(data)
|
||||
// }
|
||||
func (j *Json) CheckGet(key string) (*Json, bool) {
|
||||
m, err := j.Map()
|
||||
if err == nil {
|
||||
if val, ok := m[key]; ok {
|
||||
return &Json{val}, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Map type asserts to `map`
|
||||
func (j *Json) Map() (map[string]interface{}, error) {
|
||||
if m, ok := (j.data).(map[string]interface{}); ok {
|
||||
return m, nil
|
||||
}
|
||||
return nil, errors.New("type assertion to map[string]interface{} failed")
|
||||
}
|
||||
|
||||
// Array type asserts to an `array`
|
||||
func (j *Json) Array() ([]interface{}, error) {
|
||||
if a, ok := (j.data).([]interface{}); ok {
|
||||
return a, nil
|
||||
}
|
||||
return nil, errors.New("type assertion to []interface{} failed")
|
||||
}
|
||||
|
||||
// Bool type asserts to `bool`
|
||||
func (j *Json) Bool() (bool, error) {
|
||||
if s, ok := (j.data).(bool); ok {
|
||||
return s, nil
|
||||
}
|
||||
return false, errors.New("type assertion to bool failed")
|
||||
}
|
||||
|
||||
// String type asserts to `string`
|
||||
func (j *Json) String() (string, error) {
|
||||
if s, ok := (j.data).(string); ok {
|
||||
return s, nil
|
||||
}
|
||||
return "", errors.New("type assertion to string failed")
|
||||
}
|
||||
|
||||
// Bytes type asserts to `[]byte`
|
||||
func (j *Json) Bytes() ([]byte, error) {
|
||||
if s, ok := (j.data).(string); ok {
|
||||
return []byte(s), nil
|
||||
}
|
||||
return nil, errors.New("type assertion to []byte failed")
|
||||
}
|
||||
|
||||
// StringArray type asserts to an `array` of `string`
|
||||
func (j *Json) StringArray() ([]string, error) {
|
||||
arr, err := j.Array()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retArr := make([]string, 0, len(arr))
|
||||
for _, a := range arr {
|
||||
if a == nil {
|
||||
retArr = append(retArr, "")
|
||||
continue
|
||||
}
|
||||
s, ok := a.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("type assertion to []string failed")
|
||||
}
|
||||
retArr = append(retArr, s)
|
||||
}
|
||||
return retArr, nil
|
||||
}
|
||||
|
||||
// MustArray guarantees the return of a `[]interface{}` (with optional default)
|
||||
//
|
||||
// useful when you want to interate over array values in a succinct manner:
|
||||
//
|
||||
// for i, v := range js.Get("results").MustArray() {
|
||||
// fmt.Println(i, v)
|
||||
// }
|
||||
func (j *Json) MustArray(args ...[]interface{}) []interface{} {
|
||||
var def []interface{}
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustArray() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
a, err := j.Array()
|
||||
if err == nil {
|
||||
return a
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// MustMap guarantees the return of a `map[string]interface{}` (with optional default)
|
||||
//
|
||||
// useful when you want to interate over map values in a succinct manner:
|
||||
//
|
||||
// for k, v := range js.Get("dictionary").MustMap() {
|
||||
// fmt.Println(k, v)
|
||||
// }
|
||||
func (j *Json) MustMap(args ...map[string]interface{}) map[string]interface{} {
|
||||
var def map[string]interface{}
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustMap() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
a, err := j.Map()
|
||||
if err == nil {
|
||||
return a
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// MustString guarantees the return of a `string` (with optional default)
|
||||
//
|
||||
// useful when you explicitly want a `string` in a single value return context:
|
||||
//
|
||||
// myFunc(js.Get("param1").MustString(), js.Get("optional_param").MustString("my_default"))
|
||||
func (j *Json) MustString(args ...string) string {
|
||||
var def string
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustString() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
s, err := j.String()
|
||||
if err == nil {
|
||||
return s
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// MustStringArray guarantees the return of a `[]string` (with optional default)
|
||||
//
|
||||
// useful when you want to interate over array values in a succinct manner:
|
||||
//
|
||||
// for i, s := range js.Get("results").MustStringArray() {
|
||||
// fmt.Println(i, s)
|
||||
// }
|
||||
func (j *Json) MustStringArray(args ...[]string) []string {
|
||||
var def []string
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustStringArray() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
a, err := j.StringArray()
|
||||
if err == nil {
|
||||
return a
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// MustInt guarantees the return of an `int` (with optional default)
|
||||
//
|
||||
// useful when you explicitly want an `int` in a single value return context:
|
||||
//
|
||||
// myFunc(js.Get("param1").MustInt(), js.Get("optional_param").MustInt(5150))
|
||||
func (j *Json) MustInt(args ...int) int {
|
||||
var def int
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustInt() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
i, err := j.Int()
|
||||
if err == nil {
|
||||
return i
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// MustFloat64 guarantees the return of a `float64` (with optional default)
|
||||
//
|
||||
// useful when you explicitly want a `float64` in a single value return context:
|
||||
//
|
||||
// myFunc(js.Get("param1").MustFloat64(), js.Get("optional_param").MustFloat64(5.150))
|
||||
func (j *Json) MustFloat64(args ...float64) float64 {
|
||||
var def float64
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustFloat64() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
f, err := j.Float64()
|
||||
if err == nil {
|
||||
return f
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// MustBool guarantees the return of a `bool` (with optional default)
|
||||
//
|
||||
// useful when you explicitly want a `bool` in a single value return context:
|
||||
//
|
||||
// myFunc(js.Get("param1").MustBool(), js.Get("optional_param").MustBool(true))
|
||||
func (j *Json) MustBool(args ...bool) bool {
|
||||
var def bool
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustBool() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
b, err := j.Bool()
|
||||
if err == nil {
|
||||
return b
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// MustInt64 guarantees the return of an `int64` (with optional default)
|
||||
//
|
||||
// useful when you explicitly want an `int64` in a single value return context:
|
||||
//
|
||||
// myFunc(js.Get("param1").MustInt64(), js.Get("optional_param").MustInt64(5150))
|
||||
func (j *Json) MustInt64(args ...int64) int64 {
|
||||
var def int64
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustInt64() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
i, err := j.Int64()
|
||||
if err == nil {
|
||||
return i
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// MustUInt64 guarantees the return of an `uint64` (with optional default)
|
||||
//
|
||||
// useful when you explicitly want an `uint64` in a single value return context:
|
||||
//
|
||||
// myFunc(js.Get("param1").MustUint64(), js.Get("optional_param").MustUint64(5150))
|
||||
func (j *Json) MustUint64(args ...uint64) uint64 {
|
||||
var def uint64
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
def = args[0]
|
||||
default:
|
||||
log.Panicf("MustUint64() received too many arguments %d", len(args))
|
||||
}
|
||||
|
||||
i, err := j.Uint64()
|
||||
if err == nil {
|
||||
return i
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package simplejson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Implements the json.Unmarshaler interface.
|
||||
func (j *Json) UnmarshalJSON(p []byte) error {
|
||||
dec := json.NewDecoder(bytes.NewBuffer(p))
|
||||
dec.UseNumber()
|
||||
return dec.Decode(&j.data)
|
||||
}
|
||||
|
||||
// NewFromReader returns a *Json by decoding from an io.Reader
|
||||
func NewFromReader(r io.Reader) (*Json, error) {
|
||||
j := new(Json)
|
||||
dec := json.NewDecoder(r)
|
||||
dec.UseNumber()
|
||||
err := dec.Decode(&j.data)
|
||||
return j, err
|
||||
}
|
||||
|
||||
// Float64 coerces into a float64
|
||||
func (j *Json) Float64() (float64, error) {
|
||||
switch j.data.(type) {
|
||||
case json.Number:
|
||||
return j.data.(json.Number).Float64()
|
||||
case float32, float64:
|
||||
return reflect.ValueOf(j.data).Float(), nil
|
||||
case int, int8, int16, int32, int64:
|
||||
return float64(reflect.ValueOf(j.data).Int()), nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
return float64(reflect.ValueOf(j.data).Uint()), nil
|
||||
}
|
||||
return 0, errors.New("invalid value type")
|
||||
}
|
||||
|
||||
// Int coerces into an int
|
||||
func (j *Json) Int() (int, error) {
|
||||
switch j.data.(type) {
|
||||
case json.Number:
|
||||
i, err := j.data.(json.Number).Int64()
|
||||
return int(i), err
|
||||
case float32, float64:
|
||||
return int(reflect.ValueOf(j.data).Float()), nil
|
||||
case int, int8, int16, int32, int64:
|
||||
return int(reflect.ValueOf(j.data).Int()), nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
return int(reflect.ValueOf(j.data).Uint()), nil
|
||||
}
|
||||
return 0, errors.New("invalid value type")
|
||||
}
|
||||
|
||||
// Int64 coerces into an int64
|
||||
func (j *Json) Int64() (int64, error) {
|
||||
switch j.data.(type) {
|
||||
case json.Number:
|
||||
return j.data.(json.Number).Int64()
|
||||
case float32, float64:
|
||||
return int64(reflect.ValueOf(j.data).Float()), nil
|
||||
case int, int8, int16, int32, int64:
|
||||
return reflect.ValueOf(j.data).Int(), nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
return int64(reflect.ValueOf(j.data).Uint()), nil
|
||||
}
|
||||
return 0, errors.New("invalid value type")
|
||||
}
|
||||
|
||||
// Uint64 coerces into an uint64
|
||||
func (j *Json) Uint64() (uint64, error) {
|
||||
switch j.data.(type) {
|
||||
case json.Number:
|
||||
return strconv.ParseUint(j.data.(json.Number).String(), 10, 64)
|
||||
case float32, float64:
|
||||
return uint64(reflect.ValueOf(j.data).Float()), nil
|
||||
case int, int8, int16, int32, int64:
|
||||
return uint64(reflect.ValueOf(j.data).Int()), nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
return reflect.ValueOf(j.data).Uint(), nil
|
||||
}
|
||||
return 0, errors.New("invalid value type")
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
install:
|
||||
- go get -v .
|
||||
- go get -v golang.org/x/lint/golint
|
||||
script:
|
||||
- diff <(gofmt -d .) <(echo -n)
|
||||
- go vet -x ./...
|
||||
- golint -set_exit_status ./...
|
||||
- go test -v -race ./...
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2016, Bruce
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,143 @@
|
|||
snowflake
|
||||
====
|
||||
[](https://godoc.org/github.com/bwmarrin/snowflake) [](http://goreportcard.com/report/bwmarrin/snowflake) [](https://gocover.io/github.com/bwmarrin/snowflake) [](https://travis-ci.org/bwmarrin/snowflake) [](https://discord.gg/0f1SbxBZjYq9jLBk)
|
||||
|
||||
snowflake is a [Go](https://golang.org/) package that provides
|
||||
* A very simple Twitter snowflake generator.
|
||||
* Methods to parse existing snowflake IDs.
|
||||
* Methods to convert a snowflake ID into several other data types and back.
|
||||
* JSON Marshal/Unmarshal functions to easily use snowflake IDs within a JSON API.
|
||||
* Monotonic Clock calculations protect from clock drift.
|
||||
|
||||
**For help with this package or general Go discussion, please join the [Discord
|
||||
Gophers](https://discord.gg/0f1SbxBZjYq9jLBk) chat server.**
|
||||
|
||||
## Status
|
||||
This package should be considered stable and completed. Any additions in the
|
||||
future will strongly avoid API changes to existing functions.
|
||||
|
||||
### ID Format
|
||||
By default, the ID format follows the original Twitter snowflake format.
|
||||
* The ID as a whole is a 63 bit integer stored in an int64
|
||||
* 41 bits are used to store a timestamp with millisecond precision, using a custom epoch.
|
||||
* 10 bits are used to store a node id - a range from 0 through 1023.
|
||||
* 12 bits are used to store a sequence number - a range from 0 through 4095.
|
||||
|
||||
### Custom Format
|
||||
You can alter the number of bits used for the node id and step number (sequence)
|
||||
by setting the snowflake.NodeBits and snowflake.StepBits values. Remember that
|
||||
There is a maximum of 22 bits available that can be shared between these two
|
||||
values. You do not have to use all 22 bits.
|
||||
|
||||
### Custom Epoch
|
||||
By default this package uses the Twitter Epoch of 1288834974657 or Nov 04 2010 01:42:54.
|
||||
You can set your own epoch value by setting snowflake.Epoch to a time in milliseconds
|
||||
to use as the epoch.
|
||||
|
||||
### Custom Notes
|
||||
When setting custom epoch or bit values you need to set them prior to calling
|
||||
any functions on the snowflake package, including NewNode(). Otherwise the
|
||||
custom values you set will not be applied correctly.
|
||||
|
||||
### How it Works.
|
||||
Each time you generate an ID, it works, like this.
|
||||
* A timestamp with millisecond precision is stored using 41 bits of the ID.
|
||||
* Then the NodeID is added in subsequent bits.
|
||||
* Then the Sequence Number is added, starting at 0 and incrementing for each ID generated in the same millisecond. If you generate enough IDs in the same millisecond that the sequence would roll over or overfill then the generate function will pause until the next millisecond.
|
||||
|
||||
The default Twitter format shown below.
|
||||
```
|
||||
+--------------------------------------------------------------------------+
|
||||
| 1 Bit Unused | 41 Bit Timestamp | 10 Bit NodeID | 12 Bit Sequence ID |
|
||||
+--------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
Using the default settings, this allows for 4096 unique IDs to be generated every millisecond, per Node ID.
|
||||
## Getting Started
|
||||
|
||||
### Installing
|
||||
|
||||
This assumes you already have a working Go environment, if not please see
|
||||
[this page](https://golang.org/doc/install) first.
|
||||
|
||||
```sh
|
||||
go get github.com/bwmarrin/snowflake
|
||||
```
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
Import the package into your project then construct a new snowflake Node using a
|
||||
unique node number. The default settings permit a node number range from 0 to 1023.
|
||||
If you have set a custom NodeBits value, you will need to calculate what your
|
||||
node number range will be. With the node object call the Generate() method to
|
||||
generate and return a unique snowflake ID.
|
||||
|
||||
Keep in mind that each node you create must have a unique node number, even
|
||||
across multiple servers. If you do not keep node numbers unique the generator
|
||||
cannot guarantee unique IDs across all nodes.
|
||||
|
||||
|
||||
**Example Program:**
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a new Node with a Node number of 1
|
||||
node, err := snowflake.NewNode(1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate a snowflake ID.
|
||||
id := node.Generate()
|
||||
|
||||
// Print out the ID in a few different ways.
|
||||
fmt.Printf("Int64 ID: %d\n", id)
|
||||
fmt.Printf("String ID: %s\n", id)
|
||||
fmt.Printf("Base2 ID: %s\n", id.Base2())
|
||||
fmt.Printf("Base64 ID: %s\n", id.Base64())
|
||||
|
||||
// Print out the ID's timestamp
|
||||
fmt.Printf("ID Time : %d\n", id.Time())
|
||||
|
||||
// Print out the ID's node number
|
||||
fmt.Printf("ID Node : %d\n", id.Node())
|
||||
|
||||
// Print out the ID's sequence number
|
||||
fmt.Printf("ID Step : %d\n", id.Step())
|
||||
|
||||
// Generate and print, all in one.
|
||||
fmt.Printf("ID : %d\n", node.Generate().Int64())
|
||||
}
|
||||
```
|
||||
|
||||
### Performance
|
||||
|
||||
With default settings, this snowflake generator should be sufficiently fast
|
||||
enough on most systems to generate 4096 unique ID's per millisecond. This is
|
||||
the maximum that the snowflake ID format supports. That is, around 243-244
|
||||
nanoseconds per operation.
|
||||
|
||||
Since the snowflake generator is single threaded the primary limitation will be
|
||||
the maximum speed of a single processor on your system.
|
||||
|
||||
To benchmark the generator on your system run the following command inside the
|
||||
snowflake package directory.
|
||||
|
||||
```sh
|
||||
go test -run=^$ -bench=.
|
||||
```
|
||||
|
||||
If your curious, check out this commit that shows benchmarks that compare a few
|
||||
different ways of implementing a snowflake generator in Go.
|
||||
* https://github.com/bwmarrin/snowflake/tree/9befef8908df13f4102ed21f42b083dd862b5036
|
|
@ -0,0 +1,365 @@
|
|||
// Package snowflake provides a very simple Twitter snowflake generator and parser.
|
||||
package snowflake
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// Epoch is set to the twitter snowflake epoch of Nov 04 2010 01:42:54 UTC in milliseconds
|
||||
// You may customize this to set a different epoch for your application.
|
||||
Epoch int64 = 1288834974657
|
||||
|
||||
// NodeBits holds the number of bits to use for Node
|
||||
// Remember, you have a total 22 bits to share between Node/Step
|
||||
NodeBits uint8 = 10
|
||||
|
||||
// StepBits holds the number of bits to use for Step
|
||||
// Remember, you have a total 22 bits to share between Node/Step
|
||||
StepBits uint8 = 12
|
||||
|
||||
// DEPRECATED: the below four variables will be removed in a future release.
|
||||
mu sync.Mutex
|
||||
nodeMax int64 = -1 ^ (-1 << NodeBits)
|
||||
nodeMask = nodeMax << StepBits
|
||||
stepMask int64 = -1 ^ (-1 << StepBits)
|
||||
timeShift = NodeBits + StepBits
|
||||
nodeShift = StepBits
|
||||
)
|
||||
|
||||
const encodeBase32Map = "ybndrfg8ejkmcpqxot1uwisza345h769"
|
||||
|
||||
var decodeBase32Map [256]byte
|
||||
|
||||
const encodeBase58Map = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
|
||||
var decodeBase58Map [256]byte
|
||||
|
||||
// A JSONSyntaxError is returned from UnmarshalJSON if an invalid ID is provided.
|
||||
type JSONSyntaxError struct{ original []byte }
|
||||
|
||||
func (j JSONSyntaxError) Error() string {
|
||||
return fmt.Sprintf("invalid snowflake ID %q", string(j.original))
|
||||
}
|
||||
|
||||
// ErrInvalidBase58 is returned by ParseBase58 when given an invalid []byte
|
||||
var ErrInvalidBase58 = errors.New("invalid base58")
|
||||
|
||||
// ErrInvalidBase32 is returned by ParseBase32 when given an invalid []byte
|
||||
var ErrInvalidBase32 = errors.New("invalid base32")
|
||||
|
||||
// Create maps for decoding Base58/Base32.
|
||||
// This speeds up the process tremendously.
|
||||
func init() {
|
||||
|
||||
for i := 0; i < len(encodeBase58Map); i++ {
|
||||
decodeBase58Map[i] = 0xFF
|
||||
}
|
||||
|
||||
for i := 0; i < len(encodeBase58Map); i++ {
|
||||
decodeBase58Map[encodeBase58Map[i]] = byte(i)
|
||||
}
|
||||
|
||||
for i := 0; i < len(encodeBase32Map); i++ {
|
||||
decodeBase32Map[i] = 0xFF
|
||||
}
|
||||
|
||||
for i := 0; i < len(encodeBase32Map); i++ {
|
||||
decodeBase32Map[encodeBase32Map[i]] = byte(i)
|
||||
}
|
||||
}
|
||||
|
||||
// A Node struct holds the basic information needed for a snowflake generator
|
||||
// node
|
||||
type Node struct {
|
||||
mu sync.Mutex
|
||||
epoch time.Time
|
||||
time int64
|
||||
node int64
|
||||
step int64
|
||||
|
||||
nodeMax int64
|
||||
nodeMask int64
|
||||
stepMask int64
|
||||
timeShift uint8
|
||||
nodeShift uint8
|
||||
}
|
||||
|
||||
// An ID is a custom type used for a snowflake ID. This is used so we can
|
||||
// attach methods onto the ID.
|
||||
type ID int64
|
||||
|
||||
// NewNode returns a new snowflake node that can be used to generate snowflake
|
||||
// IDs
|
||||
func NewNode(node int64) (*Node, error) {
|
||||
|
||||
// re-calc in case custom NodeBits or StepBits were set
|
||||
// DEPRECATED: the below block will be removed in a future release.
|
||||
mu.Lock()
|
||||
nodeMax = -1 ^ (-1 << NodeBits)
|
||||
nodeMask = nodeMax << StepBits
|
||||
stepMask = -1 ^ (-1 << StepBits)
|
||||
timeShift = NodeBits + StepBits
|
||||
nodeShift = StepBits
|
||||
mu.Unlock()
|
||||
|
||||
n := Node{}
|
||||
n.node = node
|
||||
n.nodeMax = -1 ^ (-1 << NodeBits)
|
||||
n.nodeMask = n.nodeMax << StepBits
|
||||
n.stepMask = -1 ^ (-1 << StepBits)
|
||||
n.timeShift = NodeBits + StepBits
|
||||
n.nodeShift = StepBits
|
||||
|
||||
if n.node < 0 || n.node > n.nodeMax {
|
||||
return nil, errors.New("Node number must be between 0 and " + strconv.FormatInt(n.nodeMax, 10))
|
||||
}
|
||||
|
||||
var curTime = time.Now()
|
||||
// add time.Duration to curTime to make sure we use the monotonic clock if available
|
||||
n.epoch = curTime.Add(time.Unix(Epoch/1000, (Epoch%1000)*1000000).Sub(curTime))
|
||||
|
||||
return &n, nil
|
||||
}
|
||||
|
||||
// Generate creates and returns a unique snowflake ID
|
||||
// To help guarantee uniqueness
|
||||
// - Make sure your system is keeping accurate system time
|
||||
// - Make sure you never have multiple nodes running with the same node ID
|
||||
func (n *Node) Generate() ID {
|
||||
|
||||
n.mu.Lock()
|
||||
|
||||
now := time.Since(n.epoch).Nanoseconds() / 1000000
|
||||
|
||||
if now == n.time {
|
||||
n.step = (n.step + 1) & n.stepMask
|
||||
|
||||
if n.step == 0 {
|
||||
for now <= n.time {
|
||||
now = time.Since(n.epoch).Nanoseconds() / 1000000
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n.step = 0
|
||||
}
|
||||
|
||||
n.time = now
|
||||
|
||||
r := ID((now)<<n.timeShift |
|
||||
(n.node << n.nodeShift) |
|
||||
(n.step),
|
||||
)
|
||||
|
||||
n.mu.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// Int64 returns an int64 of the snowflake ID
|
||||
func (f ID) Int64() int64 {
|
||||
return int64(f)
|
||||
}
|
||||
|
||||
// ParseInt64 converts an int64 into a snowflake ID
|
||||
func ParseInt64(id int64) ID {
|
||||
return ID(id)
|
||||
}
|
||||
|
||||
// String returns a string of the snowflake ID
|
||||
func (f ID) String() string {
|
||||
return strconv.FormatInt(int64(f), 10)
|
||||
}
|
||||
|
||||
// ParseString converts a string into a snowflake ID
|
||||
func ParseString(id string) (ID, error) {
|
||||
i, err := strconv.ParseInt(id, 10, 64)
|
||||
return ID(i), err
|
||||
|
||||
}
|
||||
|
||||
// Base2 returns a string base2 of the snowflake ID
|
||||
func (f ID) Base2() string {
|
||||
return strconv.FormatInt(int64(f), 2)
|
||||
}
|
||||
|
||||
// ParseBase2 converts a Base2 string into a snowflake ID
|
||||
func ParseBase2(id string) (ID, error) {
|
||||
i, err := strconv.ParseInt(id, 2, 64)
|
||||
return ID(i), err
|
||||
}
|
||||
|
||||
// Base32 uses the z-base-32 character set but encodes and decodes similar
|
||||
// to base58, allowing it to create an even smaller result string.
|
||||
// NOTE: There are many different base32 implementations so becareful when
|
||||
// doing any interoperation.
|
||||
func (f ID) Base32() string {
|
||||
|
||||
if f < 32 {
|
||||
return string(encodeBase32Map[f])
|
||||
}
|
||||
|
||||
b := make([]byte, 0, 12)
|
||||
for f >= 32 {
|
||||
b = append(b, encodeBase32Map[f%32])
|
||||
f /= 32
|
||||
}
|
||||
b = append(b, encodeBase32Map[f])
|
||||
|
||||
for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 {
|
||||
b[x], b[y] = b[y], b[x]
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// ParseBase32 parses a base32 []byte into a snowflake ID
|
||||
// NOTE: There are many different base32 implementations so becareful when
|
||||
// doing any interoperation.
|
||||
func ParseBase32(b []byte) (ID, error) {
|
||||
|
||||
var id int64
|
||||
|
||||
for i := range b {
|
||||
if decodeBase32Map[b[i]] == 0xFF {
|
||||
return -1, ErrInvalidBase32
|
||||
}
|
||||
id = id*32 + int64(decodeBase32Map[b[i]])
|
||||
}
|
||||
|
||||
return ID(id), nil
|
||||
}
|
||||
|
||||
// Base36 returns a base36 string of the snowflake ID
|
||||
func (f ID) Base36() string {
|
||||
return strconv.FormatInt(int64(f), 36)
|
||||
}
|
||||
|
||||
// ParseBase36 converts a Base36 string into a snowflake ID
|
||||
func ParseBase36(id string) (ID, error) {
|
||||
i, err := strconv.ParseInt(id, 36, 64)
|
||||
return ID(i), err
|
||||
}
|
||||
|
||||
// Base58 returns a base58 string of the snowflake ID
|
||||
func (f ID) Base58() string {
|
||||
|
||||
if f < 58 {
|
||||
return string(encodeBase58Map[f])
|
||||
}
|
||||
|
||||
b := make([]byte, 0, 11)
|
||||
for f >= 58 {
|
||||
b = append(b, encodeBase58Map[f%58])
|
||||
f /= 58
|
||||
}
|
||||
b = append(b, encodeBase58Map[f])
|
||||
|
||||
for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 {
|
||||
b[x], b[y] = b[y], b[x]
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// ParseBase58 parses a base58 []byte into a snowflake ID
|
||||
func ParseBase58(b []byte) (ID, error) {
|
||||
|
||||
var id int64
|
||||
|
||||
for i := range b {
|
||||
if decodeBase58Map[b[i]] == 0xFF {
|
||||
return -1, ErrInvalidBase58
|
||||
}
|
||||
id = id*58 + int64(decodeBase58Map[b[i]])
|
||||
}
|
||||
|
||||
return ID(id), nil
|
||||
}
|
||||
|
||||
// Base64 returns a base64 string of the snowflake ID
|
||||
func (f ID) Base64() string {
|
||||
return base64.StdEncoding.EncodeToString(f.Bytes())
|
||||
}
|
||||
|
||||
// ParseBase64 converts a base64 string into a snowflake ID
|
||||
func ParseBase64(id string) (ID, error) {
|
||||
b, err := base64.StdEncoding.DecodeString(id)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return ParseBytes(b)
|
||||
|
||||
}
|
||||
|
||||
// Bytes returns a byte slice of the snowflake ID
|
||||
func (f ID) Bytes() []byte {
|
||||
return []byte(f.String())
|
||||
}
|
||||
|
||||
// ParseBytes converts a byte slice into a snowflake ID
|
||||
func ParseBytes(id []byte) (ID, error) {
|
||||
i, err := strconv.ParseInt(string(id), 10, 64)
|
||||
return ID(i), err
|
||||
}
|
||||
|
||||
// IntBytes returns an array of bytes of the snowflake ID, encoded as a
|
||||
// big endian integer.
|
||||
func (f ID) IntBytes() [8]byte {
|
||||
var b [8]byte
|
||||
binary.BigEndian.PutUint64(b[:], uint64(f))
|
||||
return b
|
||||
}
|
||||
|
||||
// ParseIntBytes converts an array of bytes encoded as big endian integer as
|
||||
// a snowflake ID
|
||||
func ParseIntBytes(id [8]byte) ID {
|
||||
return ID(int64(binary.BigEndian.Uint64(id[:])))
|
||||
}
|
||||
|
||||
// Time returns an int64 unix timestamp in milliseconds of the snowflake ID time
|
||||
// DEPRECATED: the below function will be removed in a future release.
|
||||
func (f ID) Time() int64 {
|
||||
return (int64(f) >> timeShift) + Epoch
|
||||
}
|
||||
|
||||
// Node returns an int64 of the snowflake ID node number
|
||||
// DEPRECATED: the below function will be removed in a future release.
|
||||
func (f ID) Node() int64 {
|
||||
return int64(f) & nodeMask >> nodeShift
|
||||
}
|
||||
|
||||
// Step returns an int64 of the snowflake step (or sequence) number
|
||||
// DEPRECATED: the below function will be removed in a future release.
|
||||
func (f ID) Step() int64 {
|
||||
return int64(f) & stepMask
|
||||
}
|
||||
|
||||
// MarshalJSON returns a json byte array string of the snowflake ID.
|
||||
func (f ID) MarshalJSON() ([]byte, error) {
|
||||
buff := make([]byte, 0, 22)
|
||||
buff = append(buff, '"')
|
||||
buff = strconv.AppendInt(buff, int64(f), 10)
|
||||
buff = append(buff, '"')
|
||||
return buff, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON converts a json byte array of a snowflake ID into an ID type.
|
||||
func (f *ID) UnmarshalJSON(b []byte) error {
|
||||
if len(b) < 3 || b[0] != '"' || b[len(b)-1] != '"' {
|
||||
return JSONSyntaxError{b}
|
||||
}
|
||||
|
||||
i, err := strconv.ParseInt(string(b[1:len(b)-1]), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*f = ID(i)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Carl Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,312 @@
|
|||
# Requests [](https://godoc.org/github.com/carlmjohnson/requests) [](https://goreportcard.com/report/github.com/carlmjohnson/requests) [](https://coveralls.io/github/carlmjohnson/requests) [](https://github.com/avelino/awesome-go)
|
||||
|
||||

|
||||
|
||||
## _HTTP requests for Gophers._
|
||||
|
||||
**The problem**: Go's net/http is powerful and versatile, but using it correctly for client requests can be extremely verbose.
|
||||
|
||||
**The solution**: The requests.Builder type is a convenient way to build, send, and handle HTTP requests. Builder has a fluent API with methods returning a pointer to the same struct, which allows for declaratively describing a request by method chaining.
|
||||
|
||||
Requests also comes with tools for building custom http transports, include a request recorder and replayer for testing.
|
||||
|
||||
## Features
|
||||
|
||||
- Simplifies HTTP client usage compared to net/http
|
||||
- Can't forget to close response body
|
||||
- Checks status codes by default
|
||||
- Supports context.Context
|
||||
- JSON serialization and deserialization helpers
|
||||
- Easily manipulate URLs and query parameters
|
||||
- Request recording and replaying for tests
|
||||
- Customizable transports and validators that are compatible with the standard library and third party libraries
|
||||
- No third party dependencies
|
||||
- Good test coverage
|
||||
|
||||
## Examples
|
||||
### Simple GET into a string
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><strong>code with net/http</strong></th>
|
||||
<th><strong>code with requests</strong></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
req, err := http.NewRequestWithContext(ctx,
|
||||
http.MethodGet, "http://example.com", nil)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
defer res.Body.Close()
|
||||
b, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
s := string(b)
|
||||
```
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```go
|
||||
var s string
|
||||
err := requests.
|
||||
URL("http://example.com").
|
||||
ToString(&s).
|
||||
Fetch(ctx)
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>11+ lines</td><td>5 lines</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
### POST a raw body
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><strong>code with net/http</strong></th>
|
||||
<th><strong>code with requests</strong></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
body := bytes.NewReader(([]byte(`hello, world`))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
"https://postman-echo.com/post", body)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
defer res.Body.Close()
|
||||
_, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```go
|
||||
err := requests.
|
||||
URL("https://postman-echo.com/post").
|
||||
BodyBytes([]byte(`hello, world`)).
|
||||
ContentType("text/plain").
|
||||
Fetch(ctx)
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>12+ lines</td><td>5 lines</td></tr></tbody></table>
|
||||
|
||||
### GET a JSON object
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><strong>code with net/http</strong></th>
|
||||
<th><strong>code with requests</strong></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
var post placeholder
|
||||
u, err := url.Parse("https://jsonplaceholder.typicode.com")
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
u.Path = fmt.Sprintf("/posts/%d", 1)
|
||||
req, err := http.NewRequestWithContext(ctx,
|
||||
http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
defer res.Body.Close()
|
||||
b, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
err := json.Unmarshal(b, &post)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
</td><td>
|
||||
|
||||
```go
|
||||
var post placeholder
|
||||
err := requests.
|
||||
URL("https://jsonplaceholder.typicode.com").
|
||||
Pathf("/posts/%d", 1).
|
||||
ToJSON(&post).
|
||||
Fetch(ctx)
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>18+ lines</td><td>7 lines</td></tr></tbody></table>
|
||||
|
||||
### POST a JSON object and parse the response
|
||||
|
||||
```go
|
||||
var res placeholder
|
||||
req := placeholder{
|
||||
Title: "foo",
|
||||
Body: "baz",
|
||||
UserID: 1,
|
||||
}
|
||||
err := requests.
|
||||
URL("/posts").
|
||||
Host("jsonplaceholder.typicode.com").
|
||||
BodyJSON(&req).
|
||||
ToJSON(&res).
|
||||
Fetch(ctx)
|
||||
// net/http equivalent left as an exercise for the reader
|
||||
```
|
||||
|
||||
### Set custom headers for a request
|
||||
|
||||
```go
|
||||
// Set headers
|
||||
var headers postman
|
||||
err := requests.
|
||||
URL("https://postman-echo.com/get").
|
||||
UserAgent("bond/james-bond").
|
||||
ContentType("secret").
|
||||
Header("martini", "shaken").
|
||||
Fetch(ctx)
|
||||
```
|
||||
|
||||
### Easily manipulate URLs and query parameters
|
||||
|
||||
```go
|
||||
u, err := requests.
|
||||
URL("https://prod.example.com/get?a=1&b=2").
|
||||
Hostf("%s.example.com", "dev1").
|
||||
Param("b", "3").
|
||||
ParamInt("c", 4).
|
||||
URL()
|
||||
if err != nil { /* ... */ }
|
||||
fmt.Println(u.String()) // https://dev1.example.com/get?a=1&b=3&c=4
|
||||
```
|
||||
|
||||
### Record and replay responses
|
||||
|
||||
```go
|
||||
// record a request to the file system
|
||||
var s1, s2 string
|
||||
err := requests.URL("http://example.com").
|
||||
Transport(requests.Record(nil, "somedir")).
|
||||
ToString(&s1).
|
||||
Fetch(ctx)
|
||||
check(err)
|
||||
|
||||
// now replay the request in tests
|
||||
err = requests.URL("http://example.com").
|
||||
Transport(requests.Replay("somedir")).
|
||||
ToString(&s2).
|
||||
Fetch(ctx)
|
||||
check(err)
|
||||
assert(s1 == s2) // true
|
||||
```
|
||||
|
||||
## FAQs
|
||||
|
||||
[See wiki](https://github.com/carlmjohnson/requests/wiki) for more details.
|
||||
|
||||
### Why not just use the standard library HTTP client?
|
||||
|
||||
Brad Fitzpatrick, long time maintainer of the net/http package, [wrote an extensive list of problems with the standard library HTTP client](https://github.com/bradfitz/exp-httpclient/blob/master/problems.md). His four main points (ignoring issues that can't be resolved by a wrapper around the standard library) are:
|
||||
|
||||
> - Too easy to not call Response.Body.Close.
|
||||
> - Too easy to not check return status codes
|
||||
> - Context support is oddly bolted on
|
||||
> - Proper usage is too many lines of boilerplate
|
||||
|
||||
Requests solves these issues by always closing the response body, checking status codes by default, always requiring a `context.Context`, and simplifying the boilerplate with a descriptive UI based on fluent method chaining.
|
||||
|
||||
### Why requests and not some other helper library?
|
||||
|
||||
There are two major flaws in other libraries as I see it. One is that in other libraries support for `context.Context` tends to be bolted on if it exists at all. Two, many hide the underlying `http.Client` in such a way that it is difficult or impossible to replace or mock out. Beyond that, I believe that none have acheived the same core simplicity that the requests library has.
|
||||
|
||||
### How do I just get some JSON?
|
||||
|
||||
```go
|
||||
var data SomeDataType
|
||||
err := requests.
|
||||
URL("https://example.com/my-json").
|
||||
ToJSON(&data).
|
||||
Fetch(ctx)
|
||||
```
|
||||
|
||||
### How do I post JSON and read the response JSON?
|
||||
|
||||
```go
|
||||
body := MyRequestType{}
|
||||
var resp MyResponseType
|
||||
err := requests.
|
||||
URL("https://example.com/my-json").
|
||||
BodyJSON(&body).
|
||||
ToJSON(&resp).
|
||||
Fetch(ctx)
|
||||
```
|
||||
|
||||
### How do I just save a file to disk?
|
||||
|
||||
It depends on exactly what you need in terms of file atomicity and buffering, but this will work for most cases:
|
||||
|
||||
```go
|
||||
err := requests.
|
||||
URL("http://example.com").
|
||||
ToFile("myfile.txt").
|
||||
Fetch(ctx)
|
||||
```
|
||||
|
||||
For more advanced use case, use `ToWriter`.
|
||||
|
||||
### How do I save a response to a string?
|
||||
|
||||
```go
|
||||
var s string
|
||||
err := requests.
|
||||
URL("http://example.com").
|
||||
ToString(&s).
|
||||
Fetch(ctx)
|
||||
```
|
||||
|
||||
### How do I validate the response status?
|
||||
|
||||
By default, if no other validators are added to a builder, requests will check that the response is in the 2XX range. If you add another validator, you can add `builder.CheckStatus(200)` or `builder.AddValidator(requests.DefaultValidator)` to the validation stack.
|
||||
|
||||
To disable all response validation, run `builder.AddValidator(nil)`.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please [create a discussion](https://github.com/carlmjohnson/requests/discussions) before submitting a pull request for a new feature.
|
|
@ -0,0 +1,9 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Only the most recently tagged version of this repository is supported.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Email me@carlmjohnson.net with any security concerns or vulnerabilities.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue