Beispiel #1
0
//PostBlobsV2Handler is
//Initiate a resumable blob upload. If successful, an upload location will be provided to complete the upload.
//Optionally, if the digest parameter is present, the request body will be used to complete the upload in a single request.
func PostBlobsV2Handler(ctx *macaron.Context) (int, []byte) {
	//TODO: If standalone == true, Dockyard will check HEADER Authorization; if standalone == false, Dockyard will check HEADER TOEKN.
	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	r := new(models.DockerV2)

	if err := r.Put(namespace, repository); err != nil {
		log.Errorf("Put or search repository error: %s", err.Error())

		result, _ := module.EncodingError(module.UNKNOWN, map[string]string{"namespace": namespace, "repository": repository})
		return http.StatusBadRequest, result
	}

	uuid := utils.MD5(uuid.NewV4().String())
	state := utils.MD5(fmt.Sprintf("%s/%s/%d", namespace, repository, time.Now().UnixNano()/int64(time.Millisecond)))
	random := fmt.Sprintf("https://%s/v2/%s/%s/blobs/uploads/%s?_state=%s",
		setting.Domains, namespace, repository, uuid, state)

	ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
	ctx.Resp.Header().Set("Docker-Upload-Uuid", uuid)
	ctx.Resp.Header().Set("Location", random)
	ctx.Resp.Header().Set("Range", "0-0")

	result, _ := json.Marshal(map[string]string{})
	return http.StatusAccepted, result
}
Beispiel #2
0
func PutRepositoryImagesV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) {
	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	r := new(models.Repository)
	if err := r.PutImages(namespace, repository); err != nil {
		log.Error("[REGISTRY API V1] Failed to save images: %v", err.Error())

		result, _ := json.Marshal(map[string]string{"message": "Failed to save images"})
		return http.StatusBadRequest, result
	}

	if ctx.Req.Header.Get("X-Docker-Token") == "true" {
		username, _, _ := utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization"))
		token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v",
			utils.MD5(username),
			namespace,
			repository,
			"write")

		ctx.Resp.Header().Set("X-Docker-Token", token)
		ctx.Resp.Header().Set("WWW-Authenticate", token)
	}

	result, _ := json.Marshal(map[string]string{})
	return http.StatusNoContent, result
}
Beispiel #3
0
func PutRepositoryV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) {
	username, _, _ := utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization"))

	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	body, err := ctx.Req.Body().String()
	if err != nil {
		log.Error("[REGISTRY API V1] Failed to get request body: %v", err.Error())
		result, _ := json.Marshal(map[string]string{"message": "Failed to get request body"})
		return http.StatusBadRequest, result
	}

	r := new(models.Repository)
	if err := r.Put(namespace, repository, body, ctx.Req.Header.Get("User-Agent"), setting.APIVERSION_V1); err != nil {
		log.Error("[REGISTRY API V1] Failed to save repository %v/%v context: %v", namespace, repository, err.Error())

		result, _ := json.Marshal(map[string]string{"message": "Failed to save repository context"})
		return http.StatusBadRequest, result
	}

	if ctx.Req.Header.Get("X-Docker-Token") == "true" {
		token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v",
			utils.MD5(username),
			namespace,
			repository,
			"write")

		ctx.Resp.Header().Set("X-Docker-Token", token)
		ctx.Resp.Header().Set("WWW-Authenticate", token)
	}

	result, _ := json.Marshal(map[string]string{})
	return http.StatusOK, result
}
Beispiel #4
0
//PutRepositoryV1Handler will create or update the repository, it's first step of Docker push.
//TODO: @1 When someone create or update the repository, it will be locked to forbidden others action include pull action.
//TODO: @2 Add a config option for allow/forbidden Docker client pull action when a repository is locked.
//TODO: @3 Intergated with [Crew](https://github.com/containerops/crew).
//TODO: @4 Token will be store in Redis, and link the push action with username@repository.
func PutRepositoryV1Handler(ctx *macaron.Context) (int, []byte) {
	var username, body string
	//var passwd string
	var err error

	if username, _, err = utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization")); err != nil {
		log.Errorf("[%s] decode Authorization error: %s", ctx.Req.RequestURI, err.Error())

		result, _ := json.Marshal(map[string]string{"Error": "Decode Authorization Error"})
		return http.StatusUnauthorized, result
	}

	//When integrated with crew, like this:
	//@1: username, passwd, _ := utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization"))
	//@2: username, passwd authorizated in Crew.

	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	//When integrated the Crew, should be check the privilage.
	if username != namespace {

	}

	//In Docker Registry V1, the repository json data in the body of `PUT /v1/:namespace/:repository`
	if body, err = ctx.Req.Body().String(); err != nil {
		log.Errorf("[%s] get repository json from http body error: %s", ctx.Req.RequestURI, err.Error())

		result, _ := json.Marshal(map[string]string{"Error": "Get Repository JSON Error"})
		return http.StatusBadRequest, result
	}

	//Create or update the repository.
	r := new(models.DockerV1)
	if e := r.Put(namespace, repository, body, ctx.Req.Header.Get("User-Agent")); e != nil {
		log.Errorf("[%s] put repository error: %s", ctx.Req.RequestURI, e.Error())

		result, _ := json.Marshal(map[string]string{"Error": "PUT Repository Error"})
		return http.StatusBadRequest, result
	}

	//If the Docker client use "X-Docker-Token", will return a randon token value.
	if ctx.Req.Header.Get("X-Docker-Token") == "true" {
		token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v",
			utils.MD5(username), namespace, repository, "write")

		ctx.Resp.Header().Set("X-Docker-Token", token)
		ctx.Resp.Header().Set("WWW-Authenticate", token)
	}

	//TODO: When deploy multi instances of dockyard, the endpoints will schedule comply all instances stauts and arithmetic.
	ctx.Resp.Header().Set("X-Docker-Endpoints", setting.Domains)

	result, _ := json.Marshal(map[string]string{})
	return http.StatusOK, result
}
Beispiel #5
0
func (b *bridge) createEvent(action string) *Event {
	event := &Event{
		ID:        utils.MD5(uuid.NewV4().String()),
		Timestamp: time.Now(),
		Action:    action,
	}
	//event.Source = b.source
	event.Actor = b.Actor
	event.Req = b.Req

	return event
}
Beispiel #6
0
func PostBlobsV2Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) {
	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	uuid := utils.MD5(uuid.NewV4().String())
	state := utils.MD5(fmt.Sprintf("%s/%s/%d", namespace, repository, time.Now().UnixNano()/int64(time.Millisecond)))
	random := fmt.Sprintf("%s://%s/v2/%s/%s/blobs/uploads/%s?_state=%s",
		setting.ListenMode,
		setting.Domains,
		namespace,
		repository,
		uuid,
		state)

	ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
	ctx.Resp.Header().Set("Docker-Upload-Uuid", uuid)
	ctx.Resp.Header().Set("Location", random)
	ctx.Resp.Header().Set("Range", "0-0")

	result, _ := json.Marshal(map[string]string{})
	return http.StatusAccepted, result
}
Beispiel #7
0
//PatchBlobsV2Handler is
//Upload a chunk of data for the specified upload.
//Docker 1.9.x above version saves layer in PATCH methord
//Docker 1.9.x below version saves layer in PUT methord
func PatchBlobsV2Handler(ctx *macaron.Context) (int, []byte) {
	repository := ctx.Params(":repository")
	namespace := ctx.Params(":namespace")

	desc := ctx.Params(":uuid")
	uuid := strings.Split(desc, "?")[0]

	if upload, err := module.CheckDockerVersion19(ctx.Req.Header.Get("User-Agent")); err != nil {
		log.Errorf("Decode docker version error: %s", err.Error())

		result, _ := module.EncodingError(module.BLOB_UPLOAD_UNKNOWN, map[string]string{"namespace": namespace, "repository": repository})
		return http.StatusBadRequest, result
	} else if upload == true {
		//It's run above docker 1.9.0
		basePath := setting.DockerV2Storage
		uuidPath := fmt.Sprintf("%s/uuid/%s", basePath, uuid)
		uuidFile := fmt.Sprintf("%s/uuid/%s/%s", basePath, uuid, uuid)

		if !utils.IsDirExist(uuidPath) {
			os.MkdirAll(uuidPath, os.ModePerm)
		}

		if _, err := os.Stat(uuidFile); err == nil {
			os.Remove(uuidFile)
		}

		if file, err := os.Create(uuidFile); err != nil {
			log.Errorf("[%s] Create UUID file error: %s", ctx.Req.RequestURI, err.Error())

			result, _ := module.EncodingError(module.BLOB_UPLOAD_UNKNOWN, map[string]string{"namespace": namespace, "repository": repository})
			return http.StatusBadRequest, result
		} else {
			io.Copy(file, ctx.Req.Request.Body)

			size, _ := utils.GetFileSize(uuidFile)

			ctx.Resp.Header().Set("Range", fmt.Sprintf("0-%v", size-1))
		}
	}

	state := utils.MD5(fmt.Sprintf("%s/%v", fmt.Sprintf("%s/%s", namespace, repository), time.Now().UnixNano()/int64(time.Millisecond)))
	random := fmt.Sprintf("https://%s/v2/%s/%s/blobs/uploads/%s?_state=%s",
		setting.Domains, namespace, repository, uuid, state)

	ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
	ctx.Resp.Header().Set("Docker-Upload-Uuid", uuid)
	ctx.Resp.Header().Set("Location", random)

	result, _ := json.Marshal(map[string]string{})
	return http.StatusOK, result
}
Beispiel #8
0
func PatchBlobsV2Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) {
	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	desc := ctx.Params(":uuid")
	uuid := strings.Split(desc, "?")[0]

	imagePathTmp := module.GetImagePath(uuid, setting.APIVERSION_V2)
	layerPathTmp := module.GetLayerPath(uuid, "layer", setting.APIVERSION_V2)

	//saving specific tarsum every times is in order to split the same tarsum in HEAD handler
	if !utils.IsDirExist(imagePathTmp) {
		os.MkdirAll(imagePathTmp, os.ModePerm)
	}

	if _, err := os.Stat(layerPathTmp); err == nil {
		os.Remove(layerPathTmp)
	}

	data, _ := ctx.Req.Body().Bytes()
	if err := ioutil.WriteFile(layerPathTmp, data, 0777); err != nil {
		log.Error("[REGISTRY API V2] Failed to save layer %v: %v", layerPathTmp, err.Error())

		result, _ := json.Marshal(map[string]string{"message": "Failed to save layer file"})
		return http.StatusInternalServerError, result
	}

	state := utils.MD5(fmt.Sprintf("%s/%s/%d", namespace, repository, time.Now().UnixNano()/int64(time.Millisecond)))
	random := fmt.Sprintf("%s://%s/v2/%s/%s/blobs/uploads/%s?_state=%s",
		setting.ListenMode,
		setting.Domains,
		namespace,
		repository,
		uuid,
		state)

	ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
	ctx.Resp.Header().Set("Docker-Upload-Uuid", uuid)
	ctx.Resp.Header().Set("Location", random)
	ctx.Resp.Header().Set("Range", fmt.Sprintf("0-%v", len(data)-1))

	result, _ := json.Marshal(map[string]string{})
	return http.StatusAccepted, result
}
Beispiel #9
0
//Support to push acis
func PostUploadHandler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) {
	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	acifile := ctx.Params(":acifile")
	signfile := fmt.Sprintf("%v%v", acifile, ".asc")

	//TODO: only for testing,pubkey will be read and saved via user management module
	pubkeyspath := module.GetPubkeysPath(namespace, repository, setting.APIVERSION_ACI)
	if _, err := os.Stat(pubkeyspath); err != nil {
		if err := os.MkdirAll(pubkeyspath, os.ModePerm); err != nil {
			log.Error("[ACI API] Failed to create pubkeys path %v: %v", pubkeyspath, err.Error())

			result, _ := json.Marshal(map[string]string{"message": "Failed to create pubkeys path"})
			return http.StatusInternalServerError, result
		}
	}

	imageId := utils.MD5(uuid.NewV4().String())
	imagepath := module.GetImagePath(imageId, setting.APIVERSION_ACI)
	if err := os.MkdirAll(imagepath, os.ModePerm); err != nil {
		log.Error("[ACI API] Failed to create aci path %v: %v", imagepath, err.Error())

		result, _ := json.Marshal(map[string]string{"message": "Failed to create aci path"})
		return http.StatusInternalServerError, result
	}

	prefix := fmt.Sprintf("%v://%v/ac/push/%v/%v/", setting.ListenMode, setting.Domains, namespace, repository)
	endpoint := models.UploadDetails{
		ACIPushVersion: "0.0.1", //TODO: follow ACI push spec
		Multipart:      false,
		ManifestURL:    prefix + imageId + "/manifest",
		SignatureURL:   prefix + imageId + "/signature/" + signfile,
		ACIURL:         prefix + imageId + "/aci/" + acifile,
		CompletedURL:   prefix + imageId + "/complete/" + acifile + "/" + signfile,
	}

	result, _ := json.Marshal(endpoint)
	return http.StatusOK, result
}
Beispiel #10
0
func GetRepositoryImagesV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) {
	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	r := new(models.Repository)
	if exists, err := r.Get(namespace, repository); err != nil {
		log.Error("[REGISTRY API V1] Failed to get repository %v/%v context: %v", namespace, repository, err.Error())

		result, _ := json.Marshal(map[string]string{"message": "Failed to get repository context"})
		return http.StatusBadRequest, result
	} else if exists == false {
		log.Error("[REGISTRY API V1] Not found repository %v/%v", namespace, repository)

		result, _ := json.Marshal(map[string]string{"message": "Not found repository"})
		return http.StatusNotFound, result
	}

	r.Download += 1

	if err := r.Save(namespace, repository); err != nil {
		log.Error("[REGISTRY API V1] Failed to save repository %v/%v context: %v", namespace, repository, err.Error())
		result, _ := json.Marshal(map[string]string{"message": "Failed to save repository context"})
		return http.StatusBadRequest, result
	}

	username, _, _ := utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization"))
	token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v",
		utils.MD5(username),
		namespace,
		repository,
		"read")

	ctx.Resp.Header().Set("X-Docker-Token", token)
	ctx.Resp.Header().Set("WWW-Authenticate", token)
	ctx.Resp.Header().Set("Content-Length", fmt.Sprint(len(r.JSON)))

	return http.StatusOK, []byte(r.JSON)
}
Beispiel #11
0
//GetRepositoryImagesV1Handler will return images json data.
func GetRepositoryImagesV1Handler(ctx *macaron.Context) (int, []byte) {
	var username string
	var err error

	if username, _, err = utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization")); err != nil {
		log.Errorf("[%s] decode Authorization error: %s", ctx.Req.RequestURI, err.Error())

		result, _ := json.Marshal(map[string]string{"Error": "Decode Authorization Error"})
		return http.StatusUnauthorized, result
	}

	namespace := ctx.Params(":namespace")
	repository := ctx.Params(":repository")

	r := new(models.DockerV1)
	if v1, err := r.Get(namespace, repository); err != nil {
		log.Errorf("[%s] get repository images data error: %s", ctx.Req.RequestURI, err.Error())

		result, _ := json.Marshal(map[string]string{"Error": "Get Repository Images Error"})
		return http.StatusBadRequest, result
	} else {

		//If the Docker client use "X-Docker-Token", will return a randon token value.
		if ctx.Req.Header.Get("X-Docker-Token") == "true" {
			token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v",
				utils.MD5(username), namespace, repository, "read")

			ctx.Resp.Header().Set("X-Docker-Token", token)
			ctx.Resp.Header().Set("WWW-Authenticate", token)
		}

		ctx.Resp.Header().Set("Content-Length", fmt.Sprint(len(v1.JSON)))

		return http.StatusOK, []byte(v1.JSON)
	}

}
Beispiel #12
0
//PutBlobsV2Handler is
//Complete the upload specified by uuid, optionally appending the body as the final chunk.
func PutBlobsV2Handler(ctx *macaron.Context) (int, []byte) {
	var size int64

	repository := ctx.Params(":repository")
	namespace := ctx.Params(":namespace")

	desc := ctx.Params(":uuid")
	uuid := strings.Split(desc, "?")[0]

	digest := ctx.Query("digest")
	tarsum := strings.Split(digest, ":")[1]

	basePath := setting.DockerV2Storage
	imagePath := fmt.Sprintf("%s/image/%s", basePath, tarsum)
	imageFile := fmt.Sprintf("%s/image/%s/%s", basePath, tarsum, tarsum)

	//Save from uuid path save too image path
	if !utils.IsDirExist(imagePath) {
		os.MkdirAll(imagePath, os.ModePerm)
	}

	if _, err := os.Stat(imageFile); err == nil {
		os.Remove(imageFile)
	}

	if upload, err := module.CheckDockerVersion19(ctx.Req.Header.Get("User-Agent")); err != nil {
		log.Errorf("Decode docker version error: %s", err.Error())

		result, _ := module.EncodingError(module.BLOB_UPLOAD_INVALID, map[string]string{"namespace": namespace, "repository": repository})
		return http.StatusBadRequest, result
	} else if upload == true {
		//Docker 1.9.x above version saves layer in PATCH method, in PUT method move from uuid to image:sha256
		uuidPath := fmt.Sprintf("%s/uuid/%s", basePath, uuid)
		uuidFile := fmt.Sprintf("%s/uuid/%s/%s", basePath, uuid, uuid)

		if _, err := os.Stat(uuidFile); err == nil {
			if err := os.Rename(uuidFile, imageFile); err != nil {
				log.Errorf("Move the temp file to image folder %s error: %s", imageFile, err.Error())

				result, _ := module.EncodingError(module.BLOB_UPLOAD_INVALID, map[string]string{"namespace": namespace, "repository": repository})
				return http.StatusBadRequest, result
			}

			size, _ = utils.GetFileSize(imagePath)

			os.RemoveAll(uuidFile)
			os.RemoveAll(uuidPath)
		}
	} else if upload == false {
		//Docker 1.9.x below version saves layer in PUT methord, save data to file directly.
		if file, err := os.Create(imageFile); err != nil {
			log.Errorf("Save the file %s error: %s", imageFile, err.Error())

			result, _ := module.EncodingError(module.BLOB_UPLOAD_INVALID, map[string]string{"namespace": namespace, "repository": repository})
			return http.StatusBadRequest, result
		} else {
			io.Copy(file, ctx.Req.Request.Body)
			size, _ = utils.GetFileSize(imagePath)
		}
	}

	i := new(models.DockerImageV2)
	if err := i.Put(tarsum, imageFile, size); err != nil {
		log.Errorf("Save the iamge data %s error: %s", tarsum, err.Error())

		result, _ := module.EncodingError(module.BLOB_UPLOAD_INVALID, map[string]string{"namespace": namespace, "repository": repository})
		return http.StatusBadRequest, result
	}

	state := utils.MD5(fmt.Sprintf("%s/%v", fmt.Sprintf("%s/%s", namespace, repository), time.Now().UnixNano()/int64(time.Millisecond)))
	random := fmt.Sprintf("https://%s/v2/%s/%s/blobs/uploads/%s?_state=%s",
		setting.Domains, namespace, repository, uuid, state)

	ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
	ctx.Resp.Header().Set("Docker-Content-Digest", digest)
	ctx.Resp.Header().Set("Location", random)

	result, _ := json.Marshal(map[string]string{})
	return http.StatusOK, result
}
Beispiel #13
0
// file : the path of file to save
func (d *radosdesc) Save(file string) (string, error) {
	fp, err := os.Open(file)
	if err != nil {
		return "", err
	}
	defer fp.Close()

	info, err := fp.Stat()
	if err != nil {
		return "", err
	}
	fileSize := uint64(info.Size())

	buf := make([]byte, d.Chunksize)
	writeUp := false
	totalRead := uint64(0)

	//get object id from Omap
	oid, err := d.getOid(file)
	if err != nil {
		oid = objectBlobPrefix + utils.MD5(uuid.NewV4().String())
		if err = d.setOid(file, oid); err != nil {
			return "", err
		}
	} else {
		// Check total object size only for existing ones
		totalSize, err := d.getXattrTotalSize(oid)
		if err != nil {
			return "", err
		}
		// If new object is smaller, delete old one
		if totalSize > fileSize {
			for offset := uint64(0); offset < totalSize; offset += d.Chunksize {
				chunkName := d.getChunkName(oid, offset)

				err = d.Ioctx.Delete(chunkName)
				if err != nil {
					return "", err
				}
			}
		}
	}

	// Write
	for {
		sizeRead := uint64(0)

		// Read from fp
		for i := 3; sizeRead < d.Chunksize; i-- {
			n, err := fp.Read(buf[sizeRead:])
			sizeRead += uint64(n)
			if err != nil {
				if err != io.EOF {
					return "", err
				}

				writeUp = true
				break
			}

			if i == 0 {
				return "", fmt.Errorf("Not read enough data")
			}
		}

		// End of file and nothing was read
		if sizeRead == 0 {
			break
		}

		// Write chunk object
		chunkName := d.getChunkName(oid, totalRead)
		if err = d.Ioctx.Write(chunkName, buf[:sizeRead], 0); err != nil {
			return "", err
		}
		totalRead += sizeRead

		// Update total object size as xattr in the first chunk of the object
		err = d.setXattrTotalSize(oid, uint64(totalRead))
		if err != nil {
			return "", err
		}

		// End of file
		if writeUp {
			break
		}
	}

	return "", nil
}