Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support registry resource #154

Merged
merged 5 commits into from
Jun 22, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions go/cmd/paddlecloud/paddlecloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ func main() {
subcommands.Register(&paddlecloud.GetCommand{}, "")
subcommands.Register(&paddlecloud.KillCommand{}, "")
subcommands.Register(&paddlecloud.SimpleFileCmd{}, "")
subcommands.Register(&pfsmod.LsCmd{}, "")
subcommands.Register(&pfsmod.CpCmd{}, "")
subcommands.Register(&pfsmod.RmCmd{}, "")
subcommands.Register(&pfsmod.MkdirCmd{}, "")
subcommands.Register(&paddlecloud.RegistryCmd{}, "")
subcommands.Register(&paddlecloud.DeleteCommand{}, "")
subcommands.Register(&pfsmod.LsCmd{}, "PFS")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the second argument "PFS" mean?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second argument of subcommands.Register is group, if we add a subcommand with a specify a group name, these subcommands will be explained with a group name before, such as:

Subcommands:
	commands         list all command names
	delete           Delete the specify resource.
	file             Simple file operations.
...

Subcommands for PFS:
	cp               uoload or download files
	ls               List files on PaddlePaddle Cloud
...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see.

subcommands.Register(&pfsmod.CpCmd{}, "PFS")
subcommands.Register(&pfsmod.RmCmd{}, "PFS")
subcommands.Register(&pfsmod.MkdirCmd{}, "PFS")

flag.Parse()
ctx := context.Background()
Expand Down
53 changes: 53 additions & 0 deletions go/paddlecloud/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package paddlecloud

import (
"context"
"flag"
"fmt"
"os"

"github.com/google/subcommands"
)

// DeleteCommand do job killings
type DeleteCommand struct {
rm bool
}

// Name is subcommands name
func (*DeleteCommand) Name() string { return "delete" }

// Synopsis is subcommands synopsis
func (*DeleteCommand) Synopsis() string { return "Delete the specify resource." }

// Usage is subcommands usage
func (*DeleteCommand) Usage() string {
return `delete registry [registry-name]
`
}

// SetFlags registers subcommands flags
func (p *DeleteCommand) SetFlags(f *flag.FlagSet) {
}

// Execute kill command
func (p *DeleteCommand) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
if f.NArg() != 2 {
f.Usage()
return subcommands.ExitFailure
}
if f.Arg(0) == RegistryCmdName {
name := f.Arg(1)
r := RegistryCmd{SecretName: KubeRegistryName(name)}
err := r.Delete()
if err != nil {
fmt.Fprintf(os.Stderr, "error delete registry: %v\n", err)
return subcommands.ExitFailure
}
fmt.Fprintf(os.Stdout, "registry: [%s] is deleted\n", name)
} else {
f.Usage()
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}
34 changes: 32 additions & 2 deletions go/paddlecloud/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (*GetCommand) Synopsis() string { return "Print resources" }

// Usage is subcommands usage
func (*GetCommand) Usage() string {
return `get [jobs|workers [jobname]|quota]:
return `get [jobs|workers|registry [jobname]|quota]:
Print resources.
`
}
Expand All @@ -47,6 +47,8 @@ func (p *GetCommand) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
jobs()
} else if f.Arg(0) == "quota" {
quota()
} else if f.Arg(0) == "registry" {
registry()
} else if f.Arg(0) == "workers" {
if f.NArg() != 2 {
f.Usage()
Expand Down Expand Up @@ -91,7 +93,35 @@ func workers(jobname string) error {
w.Flush()
return nil
}

func registry() error {
respBody, err := utils.GetCall(utils.Config.ActiveConfig.Endpoint+"/api/v1/registry/", nil)
if err != nil {
fmt.Fprintf(os.Stderr, "err getting registry secret: %v\n", err)
return err
}
var respObj interface{}
err = json.Unmarshal(respBody, &respObj)
if err != nil {
return err
}
items := respObj.(map[string]interface{})["msg"].(map[string]interface{})["items"].([]interface{})
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
if len(items) >= 0 {
fmt.Fprintf(w, "ID\tNAME\tDATA\n")
}
idx := 0
for _, r := range items {
metadata := r.(map[string]interface{})["metadata"].(map[string]interface{})
name := RegistryName(metadata["name"].(string))
if len(name) != 0 {
cTime := metadata["creation_timestamp"].(string)
fmt.Fprintf(w, "%d\t%s\t%s\n", idx, name, cTime)
idx++
}
}
w.Flush()
return err
}
func jobs() error {
respBody, err := utils.GetCall(utils.Config.ActiveConfig.Endpoint+"/api/v1/jobs/", nil)
if err != nil {
Expand Down
120 changes: 120 additions & 0 deletions go/paddlecloud/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package paddlecloud

import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"strings"

"github.com/PaddlePaddle/cloud/go/utils"
"github.com/golang/glog"
"github.com/google/subcommands"
)

const (
// RegistryCmdName is subcommand name
RegistryCmdName = "registry"
RegistryPrefix = "pcloud-registry"
)

// RegistryCmd is Docker registry secret information
type RegistryCmd struct {
SecretName string `json:"name"`
Username string `json:"username"`
Password string `json:"password"`
Server string `json:"server"`
}

// Name is the subcommand name
func (r *RegistryCmd) Name() string { return RegistryCmdName }

// Synopsis is the subcommand's synopsis
func (r *RegistryCmd) Synopsis() string { return "Add registry secret on paddlecloud." }

// Usage is the subcommand's usage
func (r *RegistryCmd) Usage() string {
return `registry <options> [add|del]:
`
}

// SetFlags registers subcommands flags.
func (r *RegistryCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&r.SecretName, "name", "", "registry secret name")
f.StringVar(&r.Username, "username", "", "your Docker registry username")
f.StringVar(&r.Password, "password", "", "your Docker registry password")
f.StringVar(&r.Server, "server", "", "your Docker registry Server")
}
func (r *RegistryCmd) addRegistrySecret() error {
jsonString, err := json.Marshal(r)
if err != nil {
return err
}
glog.V(10).Infof("Add registry secret: %s to %s\n", jsonString, utils.Config.ActiveConfig.Endpoint+"/api/v1/registry/")
respBody, err := utils.PostCall(utils.Config.ActiveConfig.Endpoint+"/api/v1/registry/", jsonString)
if err != nil {
return err
}
var respObj interface{}
if err = json.Unmarshal(respBody, &respObj); err != nil {
return err
}
// FIXME: Return an error if error message is not empty. Use response code instead
errMsg := respObj.(map[string]interface{})["msg"].(string)
if len(errMsg) > 0 {
return errors.New(errMsg)
}
return nil
}

// Delete the specify registry
func (r *RegistryCmd) Delete() error {
jsonString, err := json.Marshal(r)
if err != nil {
return err
}
glog.V(10).Infof("Delete registry secret: %s to %s\n", jsonString, utils.Config.ActiveConfig.Endpoint+"/api/v1/registry/")
respBody, err := utils.DeleteCall(utils.Config.ActiveConfig.Endpoint+"/api/v1/registry/", jsonString)
if err != nil {
return err
}

var respObj interface{}
if err = json.Unmarshal(respBody, &respObj); err != nil {
return err
}
// FIXME: Return an error if error message is not empty. Use response code instead
errMsg := respObj.(map[string]interface{})["msg"].(string)
if len(errMsg) > 0 {
return errors.New(errMsg)
}
return nil
}
func (r *RegistryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
if r.SecretName == "" || r.Username == "" || r.Password == "" || r.Server == "" {
f.Usage()
return subcommands.ExitFailure
}
r.SecretName = strings.Join([]string{RegistryPrefix, r.SecretName}, "-")
err := r.addRegistrySecret()
if err != nil {
fmt.Fprintf(os.Stderr, "add registry secret failed: %s\n", err)
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}

// KubeRegistryName add a prefix for the name
func KubeRegistryName(name string) string {
return RegistryPrefix + "-" + name
}

// RegistryName is registry secret name for PaddleCloud
func RegistryName(name string) string {
if strings.HasPrefix(name, RegistryPrefix) {
return name[len(RegistryPrefix)+1 : len(name)]
}
return ""
}
4 changes: 4 additions & 0 deletions go/paddlecloud/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type SubmitCmd struct {
Topology string `json:"topology"`
Datacenter string `json:"datacenter"`
Passes int `json:"passes"`
Image string `json:"image"`
Registry string `json:"registry"`
}

// Name is subcommands name.
Expand Down Expand Up @@ -59,6 +61,8 @@ func (p *SubmitCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&p.Entry, "entry", "", "Command of starting trainer process. Defaults to paddle train")
f.StringVar(&p.Topology, "topology", "", "Will Be Deprecated .py file contains paddle v1 job configs")
f.IntVar(&p.Passes, "passes", 1, "Pass count for training job")
f.StringVar(&p.Image, "image", "", "Runtime Docker image for the job")
f.StringVar(&p.Registry, "registry", "", "Registry secret name for the runtime Docker image")
}

// Execute submit command.
Expand Down
1 change: 1 addition & 0 deletions paddlecloud/paddlecloud/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
url(r"^api/v1/workers/", paddlejob.views.WorkersView.as_view()),
url(r"^api/v1/quota/", paddlejob.views.QuotaView.as_view()),
url(r"^api/v1/file/", paddlejob.views.SimpleFileView.as_view()),
url(r"^api/v1/registry/", paddlejob.registry.RegistryView.as_view()),
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Expand Down
3 changes: 2 additions & 1 deletion paddlecloud/paddlejob/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from paddle_job import PaddleJob
__all__ = ["PaddleJob"]
import registry
__all__ = ["PaddleJob", "registry"]
98 changes: 98 additions & 0 deletions paddlecloud/paddlejob/registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
from django.contrib import messages
from django.conf import settings
from kubernetes import client, config
from kubernetes.client.rest import ApiException
from . import PaddleJob
from rest_framework.authtoken.models import Token
from rest_framework import viewsets, generics, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.parsers import MultiPartParser, FormParser, FileUploadParser
import json
import utils
import notebook.utils
import logging
import volume
import os
import base64

def docker_cfg(username, password, email, server):
auth = "%s:%s" % (username, password)
auth_encode = base64.b64encode(auth)
return json.dumps({server:
{"username": username,
"password": password,
"email": email,
"auth": auth_encode}})

class RegistryView(APIView):
permission_classes = (permissions.IsAuthenticated,)
def post(self, request):
"""
Cretea a registry secret
"""
username = request.user.username
user_namespace = notebook.utils.email_escape(username)
api_client = notebook.utils.get_user_api_client(username)
obj = json.loads(request.body)
name = obj.get("name")
docker_username = obj.get("username")
docker_password = obj.get("password")
docker_server = obj.get("server")
cfg = docker_cfg(docker_username,
docker_password,
username,
docker_server)
try:
ret = client.CoreV1Api(
api_client=api_client).create_namespaced_secret(
namespace = user_namespace,
body = {
"apiVersion": "v1",
"kind": "Secret",
"metadata": {
"name": name
},
"data": {
".dockerconfigjson": base64.b64encode(cfg)
},
"type": "kubernetes.io/dockerconfigjson"})
except ApiException, e:
logging.error("Failed when create secret.")
return utils.simple_response(500, str(e))
return utils.simple_response(200, "")

def delete(self, request):
"""
Delete a registry secret
"""
username = username = request.user.username
user_namespace = notebook.utils.email_escape(username)
api_client = notebook.utils.get_user_api_client(username)
obj = json.loads(request.body)
name = obj.get("name")
try:
ret = client.CoreV1Api(api_client=api_client).delete_namespaced_secret(
name = name,
namespace = user_namespace,
body = client.V1DeleteOptions())
except ApiException, e:
logging.error("Failed when delete secret.")
return utils.simple_response(500, str(e))
return utils.simple_response(200, "")

def get(self, request):
"""
Get registrys
"""
username = username = request.user.username
user_namespace = notebook.utils.email_escape(username)
api_client = notebook.utils.get_user_api_client(username)
try:
secretes_list = client.CoreV1Api(api_client=api_client).list_namespaced_secret(
namespace=user_namespace)
return utils.simple_response(200, secretes_list.to_dict())
except ApiException, e:
logging.error("Failed when list secrets.")
return utils.simple_response(500, str(e))
7 changes: 4 additions & 3 deletions paddlecloud/paddlejob/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ def post(self, request, format=None):
))
else:
pass

registry_secret = settings.JOB_DOCKER_IMAGE.get("registry_secret", None)
registry_secret = obj.get("registry", None)
if not registry_secret:
registry_secret = settings.JOB_DOCKER_IMAGE.get("registry_secret", None)
# get user specified image
job_image = obj.get("image", None)
gpu_count = obj.get("gpu", 0)
Expand Down Expand Up @@ -288,7 +289,7 @@ def get(self, request, format=None):
username = request.user.username
namespace = notebook.utils.email_escape(username)
api_client = notebook.utils.get_user_api_client(username)
quota_list = api_client.CoreV1Api(api_cilent=api_client)\
quota_list = client.CoreV1Api(api_client=api_client)\
.list_namespaced_resource_quota(namespace)
return Response(quota_list.to_dict())

Expand Down