Initial commit
This commit is contained in:
commit
0511a71fea
|
@ -0,0 +1,5 @@
|
||||||
|
/deb-simple
|
||||||
|
/repo
|
||||||
|
/src
|
||||||
|
conf.json
|
||||||
|
*.pprof
|
|
@ -0,0 +1,16 @@
|
||||||
|
image: knightswarm/gobuild:1.6
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- go get github.com/tools/godep
|
||||||
|
- export GOPATH=$(pwd)
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
|
||||||
|
compile:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- export DEB_SIMPLE_VERSION=`grep "var VERSION" main.go | awk -F\" '{print $2}'`
|
||||||
|
- godep restore
|
||||||
|
- chmod +x ./ci/package.sh
|
||||||
|
- ./ci/package.sh
|
|
@ -0,0 +1,12 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.5
|
||||||
|
before_install:
|
||||||
|
- go get github.com/blakesmith/ar
|
||||||
|
- go get github.com/axw/gocov/gocov
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||||
|
- go test -run=XXX -bench=.
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) [year] [fullname]
|
||||||
|
|
||||||
|
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,10 @@
|
||||||
|
all:
|
||||||
|
go build -o deb-simple .
|
||||||
|
|
||||||
|
install:
|
||||||
|
mkdir -p $(DESTDIR)/usr/bin
|
||||||
|
cp deb-simple $(DESTDIR)/usr/bin
|
||||||
|
chmod 755 $(DESTDIR)/usr/bin/deb-simple
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f deb-simple
|
|
@ -0,0 +1,52 @@
|
||||||
|
[![Build Status](https://travis-ci.org/esell/deb-simple.svg?branch=master)](https://travis-ci.org/esell/deb-simple)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/esell/deb-simple/badge.svg?branch=master)](https://coveralls.io/github/esell/deb-simple?branch=master)
|
||||||
|
|
||||||
|
|
||||||
|
# deb-simple (get it? dead simple.. deb simple...)
|
||||||
|
|
||||||
|
A lightweight, bare-bones apt repository server.
|
||||||
|
|
||||||
|
# Purpose
|
||||||
|
|
||||||
|
This project came from a need I had to be able to serve up already created deb packages without a lot of fuss. Most of the existing solutions
|
||||||
|
I found were either geared at mirroring existing "official" repos or for providing your packages to the public. My need was just something that
|
||||||
|
I could use internally to install already built deb packages via apt-get. I didn't care about change files, signed packages, etc. Since this was
|
||||||
|
to be used in a CI pipeline it had to support remote uploads and be able to update the package list after each upload.
|
||||||
|
|
||||||
|
# What it does:
|
||||||
|
|
||||||
|
- Supports multiple versions of packages
|
||||||
|
- Supports multi-arch repos (i386, amd64, custom, etc)
|
||||||
|
- Supports uploading via HTTP/HTTPS POST requests
|
||||||
|
- Supports removing packages via HTTP/HTTPS DELETE requests
|
||||||
|
- Does NOT require a changes file
|
||||||
|
- Supports uploads from various locations without corrupting the repo
|
||||||
|
|
||||||
|
|
||||||
|
# What it doesn't do:
|
||||||
|
- Create actual packages
|
||||||
|
- Mirror existing repos
|
||||||
|
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
|
||||||
|
Install using `go get`. Fill out the conf.json file with the values you want, it should be pretty self-explanatory, then fire it up!
|
||||||
|
|
||||||
|
Once it is running POST a file to the `/upload` endpoint:
|
||||||
|
|
||||||
|
`curl -XPOST 'http://localhost:9090/upload?arch=amd64&distro=stable' -F "file=@myapp.deb"`
|
||||||
|
|
||||||
|
Or delete an existing file:
|
||||||
|
|
||||||
|
`curl -XDELETE 'http://localhost:9090/delete' -d '{"filename":"myapp.deb","distroName":"stable","arch":"amd64"}'`
|
||||||
|
|
||||||
|
To use your new repo you will have to add a line like this to your sources.list file:
|
||||||
|
|
||||||
|
`deb http://my-hostname:listenPort/ stable main`
|
||||||
|
|
||||||
|
`my-hostname` should be the actual hostname/IP where you are running deb-simple and `listenPort` will be whatever you set in the config. By default deb-simple puts everything into the `stable` distro and `main` section. If you have enabled SSL you will want to swap `http` for `https`.
|
||||||
|
|
||||||
|
|
||||||
|
#License:
|
||||||
|
|
||||||
|
[MIT](LICENSE.txt) so go crazy. Would appreciate PRs for anything cool you add though :)
|
|
@ -0,0 +1,306 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"fmt"
|
||||||
|
"github.com/blakesmith/ar"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"compress/gzip"
|
||||||
|
"archive/tar"
|
||||||
|
"log"
|
||||||
|
"path/filepath"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"bufio"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func inspectPackage(filename string) (string, error) {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error opening package file %s: %s", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
arReader := ar.NewReader(f)
|
||||||
|
defer f.Close()
|
||||||
|
var controlBuf bytes.Buffer
|
||||||
|
|
||||||
|
for {
|
||||||
|
header, err := arReader.Next()
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error in inspectPackage loop: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Trim(header.Name, "/") == "control.tar.gz" {
|
||||||
|
io.Copy(&controlBuf, arReader)
|
||||||
|
return inspectPackageControl(controlBuf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspectPackageControl(filename bytes.Buffer) (string, error) {
|
||||||
|
gzf, err := gzip.NewReader(bytes.NewReader(filename.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error creating gzip reader: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tarReader := tar.NewReader(gzf)
|
||||||
|
var controlBuf bytes.Buffer
|
||||||
|
for {
|
||||||
|
header, err := tarReader.Next()
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to inspect package: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := header.Name
|
||||||
|
|
||||||
|
switch header.Typeflag {
|
||||||
|
case tar.TypeDir:
|
||||||
|
continue
|
||||||
|
case tar.TypeReg:
|
||||||
|
if name == "./control" {
|
||||||
|
io.Copy(&controlBuf, tarReader)
|
||||||
|
return controlBuf.String(), nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Printf(
|
||||||
|
"Unable to figure out type : %c in file %s\n",
|
||||||
|
header.Typeflag, name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPackagesGz(config Conf, distro, arch string) error {
|
||||||
|
stdFile, err := os.Create(filepath.Join(config.ArchPath(distro, arch), "Packages"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create packages: %s", err)
|
||||||
|
}
|
||||||
|
defer stdFile.Close()
|
||||||
|
|
||||||
|
gzipFile, err := os.Create(filepath.Join(config.ArchPath(distro, arch), "Packages.gz"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create packages.gz: %s", err)
|
||||||
|
}
|
||||||
|
defer gzipFile.Close()
|
||||||
|
|
||||||
|
gzWriter := gzip.NewWriter(gzipFile)
|
||||||
|
defer gzWriter.Close()
|
||||||
|
|
||||||
|
stdOut := bufio.NewWriter(stdFile)
|
||||||
|
|
||||||
|
// loop through each directory
|
||||||
|
// run inspectPackage
|
||||||
|
|
||||||
|
poolPath := config.PoolPath(distro, arch)
|
||||||
|
|
||||||
|
dirList, err := ioutil.ReadDir(poolPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("scanning: %s: %s", poolPath, err)
|
||||||
|
}
|
||||||
|
for _, firstChar := range dirList {
|
||||||
|
// Recursing isn't fun...
|
||||||
|
dirList, err := ioutil.ReadDir(filepath.Join(poolPath, firstChar.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("scanning: %s: %s", poolPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range dirList {
|
||||||
|
dirList, err := ioutil.ReadDir(filepath.Join(poolPath, firstChar.Name(), p.Name()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("scanning: %s: %s", poolPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, debFile := range dirList {
|
||||||
|
if strings.HasSuffix(debFile.Name(), "deb") {
|
||||||
|
var packBuf bytes.Buffer
|
||||||
|
debName := packageName(debFile.Name())
|
||||||
|
debPath := filepath.Join(config.PoolPackagePath(distro, arch, debFile.Name()), debFile.Name())
|
||||||
|
tempCtlData, err := inspectPackage(debPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
packBuf.WriteString(tempCtlData)
|
||||||
|
|
||||||
|
var size int64 = debFile.Size()
|
||||||
|
|
||||||
|
if size == 0 {
|
||||||
|
if stat, err := os.Stat(debPath); err == nil {
|
||||||
|
size = stat.Size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := filepath.Join("pool/main", distro, arch, debName[0:1], debName, debFile.Name())
|
||||||
|
log.Println("path:", dir)
|
||||||
|
fmt.Fprintf(&packBuf, "Filename: %s\n", strings.Replace(dir, "\\", "/", -1))
|
||||||
|
fmt.Fprintf(&packBuf, "Size: %d\n", size)
|
||||||
|
|
||||||
|
f, err := os.Open(debPath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error opening deb file: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
md5hash = md5.New()
|
||||||
|
sha1hash = sha1.New()
|
||||||
|
sha256hash = sha256.New()
|
||||||
|
)
|
||||||
|
|
||||||
|
f.Seek(0, 0)
|
||||||
|
if _, err := io.Copy(md5hash, f); err != nil {
|
||||||
|
log.Println("error with the md5 hash: ", err)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&packBuf, "MD5sum: %s\n",
|
||||||
|
hex.EncodeToString(md5hash.Sum(nil)))
|
||||||
|
f.Seek(0, 0)
|
||||||
|
if _, err = io.Copy(sha1hash, f); err != nil {
|
||||||
|
log.Println("error with the sha1 hash: ", err)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&packBuf, "SHA1: %s\n",
|
||||||
|
hex.EncodeToString(sha1hash.Sum(nil)))
|
||||||
|
f.Seek(0, 0)
|
||||||
|
if _, err = io.Copy(sha256hash, f); err != nil {
|
||||||
|
log.Println("error with the sha256 hash: ", err)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&packBuf, "SHA256: %s\n",
|
||||||
|
hex.EncodeToString(sha256hash.Sum(nil)))
|
||||||
|
packBuf.WriteString("\n\n")
|
||||||
|
|
||||||
|
stdOut.Write(packBuf.Bytes())
|
||||||
|
gzWriter.Write(packBuf.Bytes())
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
f = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdOut.Flush()
|
||||||
|
|
||||||
|
gzWriter.Flush()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRelease(config Conf, distro, arch string) error {
|
||||||
|
outfile, err := os.Create(filepath.Join(config.DistPath(distro), "Release"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create Release: %s", err)
|
||||||
|
}
|
||||||
|
defer outfile.Close()
|
||||||
|
|
||||||
|
var packBuf bytes.Buffer
|
||||||
|
fmt.Fprintf(&packBuf, "Suite: %s\n", distro)
|
||||||
|
fmt.Fprintf(&packBuf, "Architectures: %s\n", arch)
|
||||||
|
fmt.Fprint(&packBuf, "Components: main\n")
|
||||||
|
fmt.Fprintf(&packBuf, "Date: %s\n", time.Now().In(time.UTC).Format("Mon, 02 Jan 2006 15:04:05 -0700"))
|
||||||
|
|
||||||
|
basePath := filepath.Join("main", "binary-" + arch)
|
||||||
|
|
||||||
|
dirList, err := ioutil.ReadDir(filepath.Join(config.DistPath(distro), "main", "binary-" + arch))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("scanning: %s: %s", config.PoolPath(distro, arch), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var md5Buf bytes.Buffer
|
||||||
|
var sha1Buf bytes.Buffer
|
||||||
|
var sha256Buf bytes.Buffer
|
||||||
|
|
||||||
|
for _, file := range dirList {
|
||||||
|
filePath := filepath.Join(config.DistPath(distro), basePath, file.Name())
|
||||||
|
|
||||||
|
log.Println("File path:", filePath)
|
||||||
|
|
||||||
|
fileLocalPath := filepath.Join(basePath, file.Name())
|
||||||
|
|
||||||
|
fileLocalPath = strings.Replace(fileLocalPath, "\\", "/", -1)
|
||||||
|
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error opening source file: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var size int64 = file.Size()
|
||||||
|
|
||||||
|
if size == 0 {
|
||||||
|
if stat, err := os.Stat(filePath); err == nil {
|
||||||
|
size = stat.Size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
md5hash = md5.New()
|
||||||
|
sha1hash = sha1.New()
|
||||||
|
sha256hash = sha256.New()
|
||||||
|
)
|
||||||
|
|
||||||
|
f.Seek(0, 0)
|
||||||
|
if _, err := io.Copy(md5hash, f); err != nil {
|
||||||
|
log.Println("error with the md5 hashing: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(&md5Buf, " %s %d %s\n", hex.EncodeToString(md5hash.Sum(nil)), size, fileLocalPath)
|
||||||
|
|
||||||
|
f.Seek(0, 0)
|
||||||
|
if _, err := io.Copy(sha1hash, f); err != nil {
|
||||||
|
log.Println("error with the sha1 hashing: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(&sha1Buf, " %s %d %s\n", hex.EncodeToString(sha1hash.Sum(nil)), size, fileLocalPath)
|
||||||
|
|
||||||
|
f.Seek(0, 0)
|
||||||
|
if _, err := io.Copy(sha256hash, f); err != nil {
|
||||||
|
log.Println("error with the sha256 hashing: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(&sha256Buf, " %s %d %s\n", hex.EncodeToString(sha256hash.Sum(nil)), size, fileLocalPath)
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
f = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(&packBuf, "MD5Sum:\n%s", string(md5Buf.Bytes()))
|
||||||
|
fmt.Fprintf(&packBuf, "SHA1:\n%s", string(sha1Buf.Bytes()))
|
||||||
|
fmt.Fprintf(&packBuf, "SHA256:\n%s", string(sha256Buf.Bytes()))
|
||||||
|
|
||||||
|
outfile.Write(packBuf.Bytes())
|
||||||
|
|
||||||
|
if pgpEntity != nil {
|
||||||
|
gpgfile, err := os.Create(filepath.Join(config.DistPath(distro), "Release.gpg"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create Release.gpg: %s", err)
|
||||||
|
}
|
||||||
|
defer gpgfile.Close()
|
||||||
|
|
||||||
|
byteReader := bytes.NewReader(packBuf.Bytes())
|
||||||
|
|
||||||
|
if err := openpgp.ArmoredDetachSignText(gpgfile, pgpEntity, byteReader, nil); err != nil {
|
||||||
|
return fmt.Errorf("failed to sign Release: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
curl -s -X POST -F "file=@debian/changelog" -F "name=deb-simple" -F "version=$DEB_SIMPLE_VERSION" -F "author=Gitlab CI" -F "email=gitlab@knightswarm.com" -F "changes[]=Auto build for $CI_BUILD_REF" https://api.meow.tf/changelog/update > debian/changelog
|
||||||
|
|
||||||
|
if [ ! -z "$GITLAB_CI_PUT_URL" ]; then
|
||||||
|
curl -s -X PUT -F "file_path=debian/changelog" -F branch_name=master -F "commit_message=[ci skip] Update changelog for $CI_BUILD_REF" -F "content=<debian/changelog" $GITLABCI_PUT_URL
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x debian/rules
|
||||||
|
fakeroot debian/rules clean binary
|
||||||
|
|
||||||
|
if [ ! -z "$UPLOAD_URL" ]; then
|
||||||
|
FILE=`ls ./build | head -n 1`
|
||||||
|
curl -s -X POST "$UPLOAD_URL" -F "file=@build/$FILE"
|
||||||
|
fi
|
|
@ -0,0 +1,5 @@
|
||||||
|
deb-simple (1.0.0) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Initial build
|
||||||
|
|
||||||
|
-- Gitlab CI <gitlab@knightswarm.com> Fri, 25 Mar 2016 21:53:18 +0000
|
|
@ -0,0 +1 @@
|
||||||
|
9
|
|
@ -0,0 +1,13 @@
|
||||||
|
Source: deb-simple
|
||||||
|
Maintainer: Tyler Stuyfzand <tyler.stuyfzand@knightswarm.com>
|
||||||
|
Section: misc
|
||||||
|
Priority: optional
|
||||||
|
Standards-Version: 3.9.2
|
||||||
|
Build-Depends: debhelper (>= 9)
|
||||||
|
|
||||||
|
Package: deb-simple
|
||||||
|
Architecture: any
|
||||||
|
Depends: ${misc:Depends}
|
||||||
|
Description: Dead Simple Debian Repository
|
||||||
|
A debian repository server which supports file uploads
|
||||||
|
and package signing
|
|
@ -0,0 +1,4 @@
|
||||||
|
usr/bin
|
||||||
|
etc
|
||||||
|
opt/deb-simple
|
||||||
|
opt/deb-simple/repo
|
|
@ -0,0 +1,159 @@
|
||||||
|
#! /bin/sh
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: deb-simple
|
||||||
|
# Required-Start: $remote_fs $syslog
|
||||||
|
# Required-Stop: $remote_fs $syslog
|
||||||
|
# Default-Start: 2 3 4 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
# Short-Description: Debian Package Server
|
||||||
|
# Description: Dead Simple Debian Package Server written in Go
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# Author: Tyler Stuyfzand <tyler.stuyfzand@knightswarm.com>
|
||||||
|
#
|
||||||
|
# Please remove the "Author" lines above and replace them
|
||||||
|
# with your own name if you copy and modify this script.
|
||||||
|
|
||||||
|
# Do NOT "set -e"
|
||||||
|
|
||||||
|
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||||
|
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
||||||
|
DESC="Simple Debian Package Server"
|
||||||
|
NAME=deb-simple
|
||||||
|
USER=deb-simple
|
||||||
|
DAEMON=/usr/bin/$NAME
|
||||||
|
DAEMON_ARGS="-c /etc/deb-simple.conf"
|
||||||
|
PIDFILE=/var/run/$NAME.pid
|
||||||
|
SCRIPTNAME=/etc/init.d/$NAME
|
||||||
|
|
||||||
|
# Exit if the package is not installed
|
||||||
|
[ -x "$DAEMON" ] || exit 0
|
||||||
|
|
||||||
|
# Read configuration variable file if it is present
|
||||||
|
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||||
|
|
||||||
|
# Load the VERBOSE setting and other rcS variables
|
||||||
|
. /lib/init/vars.sh
|
||||||
|
|
||||||
|
# Define LSB log_* functions.
|
||||||
|
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||||
|
# and status_of_proc is working.
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
|
#
|
||||||
|
# Function that starts the daemon/service
|
||||||
|
#
|
||||||
|
do_start()
|
||||||
|
{
|
||||||
|
# Return
|
||||||
|
# 0 if daemon has been started
|
||||||
|
# 1 if daemon was already running
|
||||||
|
# 2 if daemon could not be started
|
||||||
|
start-stop-daemon --start --quiet --chuid $USER:$USER --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
||||||
|
|| return 1
|
||||||
|
start-stop-daemon --start --quiet --chuid $USER:$USER --pidfile $PIDFILE --make-pidfile --background --exec $DAEMON -- \
|
||||||
|
$DAEMON_ARGS \
|
||||||
|
|| return 2
|
||||||
|
# Add code here, if necessary, that waits for the process to be ready
|
||||||
|
# to handle requests from services started subsequently which depend
|
||||||
|
# on this one. As a last resort, sleep for some time.
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Function that stops the daemon/service
|
||||||
|
#
|
||||||
|
do_stop()
|
||||||
|
{
|
||||||
|
# Return
|
||||||
|
# 0 if daemon has been stopped
|
||||||
|
# 1 if daemon was already stopped
|
||||||
|
# 2 if daemon could not be stopped
|
||||||
|
# other if a failure occurred
|
||||||
|
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
|
||||||
|
RETVAL="$?"
|
||||||
|
[ "$RETVAL" = 2 ] && return 2
|
||||||
|
# Wait for children to finish too if this is a daemon that forks
|
||||||
|
# and if the daemon is only ever run from this initscript.
|
||||||
|
# If the above conditions are not satisfied then add some other code
|
||||||
|
# that waits for the process to drop all resources that could be
|
||||||
|
# needed by services started subsequently. A last resort is to
|
||||||
|
# sleep for some time.
|
||||||
|
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
|
||||||
|
[ "$?" = 2 ] && return 2
|
||||||
|
# Many daemons don't delete their pidfiles when they exit.
|
||||||
|
rm -f $PIDFILE
|
||||||
|
return "$RETVAL"
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Function that sends a SIGHUP to the daemon/service
|
||||||
|
#
|
||||||
|
do_reload() {
|
||||||
|
#
|
||||||
|
# If the daemon can reload its configuration without
|
||||||
|
# restarting (for example, when it is sent a SIGHUP),
|
||||||
|
# then implement that here.
|
||||||
|
#
|
||||||
|
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||||
|
do_start
|
||||||
|
case "$?" in
|
||||||
|
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||||
|
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||||
|
do_stop
|
||||||
|
case "$?" in
|
||||||
|
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||||
|
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||||
|
;;
|
||||||
|
#reload|force-reload)
|
||||||
|
#
|
||||||
|
# If do_reload() is not implemented then leave this commented out
|
||||||
|
# and leave 'force-reload' as an alias for 'restart'.
|
||||||
|
#
|
||||||
|
#log_daemon_msg "Reloading $DESC" "$NAME"
|
||||||
|
#do_reload
|
||||||
|
#log_end_msg $?
|
||||||
|
#;;
|
||||||
|
restart|force-reload)
|
||||||
|
#
|
||||||
|
# If the "reload" option is implemented then remove the
|
||||||
|
# 'force-reload' alias
|
||||||
|
#
|
||||||
|
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||||
|
do_stop
|
||||||
|
case "$?" in
|
||||||
|
0|1)
|
||||||
|
do_start
|
||||||
|
case "$?" in
|
||||||
|
0) log_end_msg 0 ;;
|
||||||
|
1) log_end_msg 1 ;; # Old process is still running
|
||||||
|
*) log_end_msg 1 ;; # Failed to start
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Failed to stop
|
||||||
|
log_end_msg 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
|
||||||
|
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||||
|
exit 3
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
:
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if test "$1" = "configure"; then
|
||||||
|
if ! getent passwd deb-simple > /dev/null; then
|
||||||
|
adduser --quiet --system --group \
|
||||||
|
--home /opt/deb-simple \
|
||||||
|
--no-create-home \
|
||||||
|
deb-simple
|
||||||
|
fi
|
||||||
|
mkdir -p /opt/deb-simple/repo || true
|
||||||
|
chown -R deb-simple:deb-simple /opt/deb-simple
|
||||||
|
fi
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/make -f
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
dh_auto_install
|
||||||
|
install -d debian/deb-simple/opt/deb-simple
|
||||||
|
install -d debian/deb-simple/opt/deb-simple/repo
|
||||||
|
install -m 644 sample_conf.json debian/deb-simple/etc/deb-simple.conf
|
||||||
|
|
||||||
|
override_dh_builddeb:
|
||||||
|
mkdir ./build || true
|
||||||
|
dh_builddeb --destdir=./build --
|
|
@ -0,0 +1 @@
|
||||||
|
3.0 (quilt)
|
|
@ -0,0 +1,16 @@
|
||||||
|
hash: f49345e0dcd6cb4f3c49af64a95afbea2e608503cd6c1909d300e4a457ef65a6
|
||||||
|
updated: 2017-05-02T21:07:03.8312824-04:00
|
||||||
|
imports:
|
||||||
|
- name: github.com/blakesmith/ar
|
||||||
|
version: 8bd4349a67f2533b078dbc524689d15dba0f4659
|
||||||
|
- name: golang.org/x/crypto
|
||||||
|
version: d1464577745bc7f4e74f65be9cfbd09436a729d6
|
||||||
|
subpackages:
|
||||||
|
- cast5
|
||||||
|
- openpgp
|
||||||
|
- openpgp/armor
|
||||||
|
- openpgp/elgamal
|
||||||
|
- openpgp/errors
|
||||||
|
- openpgp/packet
|
||||||
|
- openpgp/s2k
|
||||||
|
testImports: []
|
|
@ -0,0 +1,6 @@
|
||||||
|
package: .
|
||||||
|
import:
|
||||||
|
- package: github.com/blakesmith/ar
|
||||||
|
- package: golang.org/x/crypto
|
||||||
|
subpackages:
|
||||||
|
- openpgp
|
|
@ -0,0 +1,129 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"log"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func uploadHandler(config Conf) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "POST" {
|
||||||
|
http.Error(w, "method not supported", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
archType := r.URL.Query().Get("arch")
|
||||||
|
if archType == "" {
|
||||||
|
archType = "all"
|
||||||
|
}
|
||||||
|
distroName := r.URL.Query().Get("distro")
|
||||||
|
if distroName == "" {
|
||||||
|
distroName = "stable"
|
||||||
|
}
|
||||||
|
key := r.URL.Query().Get("key")
|
||||||
|
if key == "" || key != config.Key {
|
||||||
|
http.Error(w, "unauthorized", 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reader, err := r.MultipartReader()
|
||||||
|
if err != nil {
|
||||||
|
httpErrorf(w, "error creating multipart reader: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
part, err := reader.NextPart()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if part.FileName() == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newPath := config.PoolPackagePath(distroName, archType, part.FileName())
|
||||||
|
|
||||||
|
if _, err := os.Stat(newPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(newPath, 0755); err != nil {
|
||||||
|
httpErrorf(w, "error creating path: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst, err := os.Create(filepath.Join(newPath, part.FileName()))
|
||||||
|
if err != nil {
|
||||||
|
httpErrorf(w, "error creating deb file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(dst, part); err != nil {
|
||||||
|
httpErrorf(w, "error writing deb file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
log.Println("got lock, updating package list...")
|
||||||
|
if err := createPackagesGz(config, distroName, archType); err != nil {
|
||||||
|
httpErrorf(w, "error creating package: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createRelease(config, distroName, archType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
httpErrorf(w, "error creating package: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHandler(config Conf) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "DELETE" {
|
||||||
|
http.Error(w, "method not supported", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var toDelete DeleteObj
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&toDelete); err != nil {
|
||||||
|
httpErrorf(w, "failed to decode json: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
debPath := filepath.Join(config.ArchPath(toDelete.DistroName, toDelete.Arch), toDelete.Filename)
|
||||||
|
if err := os.Remove(debPath); err != nil {
|
||||||
|
httpErrorf(w, "failed to delete: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key := r.URL.Query().Get("key")
|
||||||
|
if key == "" || key != config.Key {
|
||||||
|
http.Error(w, "unauthorized", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
log.Println("got lock, updating package list...")
|
||||||
|
if err := createPackagesGz(config, toDelete.DistroName, toDelete.Arch); err != nil {
|
||||||
|
httpErrorf(w, "failed to delete package: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := createRelease(config, toDelete.DistroName, toDelete.Arch); err != nil {
|
||||||
|
httpErrorf(w, "failed to delete package: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpErrorf(w http.ResponseWriter, format string, a ...interface{}) {
|
||||||
|
err := fmt.Errorf(format, a...)
|
||||||
|
log.Println(err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
"strings"
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"runtime"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var VERSION string = "1.1.0"
|
||||||
|
|
||||||
|
type Conf struct {
|
||||||
|
ListenPort string `json:"listenPort"`
|
||||||
|
RootRepoPath string `json:"rootRepoPath"`
|
||||||
|
SupportArch []string `json:"supportedArch"`
|
||||||
|
DistroNames []string `json:"distroNames"`
|
||||||
|
PGPSecretKey string `json:"pgpSecretKey"`
|
||||||
|
PGPPassphrase string `json:"pgpPassphrase"`
|
||||||
|
EnableSSL bool `json:"enableSSL"`
|
||||||
|
SSLCert string `json:"SSLcert"`
|
||||||
|
SSLKey string `json:"SSLkey"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conf) DistPath(distro string) string {
|
||||||
|
return filepath.Join(c.RootRepoPath, "dists", distro)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conf) ArchPath(distro, arch string) string {
|
||||||
|
return filepath.Join(c.RootRepoPath, "dists", distro, "main/binary-"+arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conf) PoolPath(distro, arch string) string {
|
||||||
|
return filepath.Join(c.RootRepoPath, "pool/main", distro, arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conf) PoolPackagePath(distro, arch, name string) string {
|
||||||
|
name = packageName(name)
|
||||||
|
return filepath.Join(c.RootRepoPath, "pool/main", distro, arch, name[0:1], name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageName(name string) string {
|
||||||
|
if index := strings.Index(name, "_"); index != -1 {
|
||||||
|
name = name[:index]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteObj struct {
|
||||||
|
Filename string
|
||||||
|
DistroName string
|
||||||
|
Arch string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mutex sync.Mutex
|
||||||
|
configFile = flag.String("c", "conf.json", "config file location")
|
||||||
|
flagShowVersion = flag.Bool("version", false, "Show dnsconfig version")
|
||||||
|
parsedConfig = Conf{}
|
||||||
|
pgpEntity *openpgp.Entity
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *flagShowVersion {
|
||||||
|
fmt.Printf("deb-simple %s (%s)\n", VERSION, runtime.Version())
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := ioutil.ReadFile(*configFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("unable to read config file, exiting...")
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(file, &parsedConfig); err != nil {
|
||||||
|
log.Fatalln("unable to marshal config file, exiting...")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := createDirs(parsedConfig); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
log.Fatalln("error creating directory structure, exiting")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := setupPgp(parsedConfig); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
log.Fatalln("error loading pgp key, exiting")
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(parsedConfig.RootRepoPath))))
|
||||||
|
http.Handle("/upload", uploadHandler(parsedConfig))
|
||||||
|
http.Handle("/delete", deleteHandler(parsedConfig))
|
||||||
|
|
||||||
|
if parsedConfig.EnableSSL {
|
||||||
|
log.Println("running with SSL enabled")
|
||||||
|
log.Fatalln(http.ListenAndServeTLS(":"+parsedConfig.ListenPort, parsedConfig.SSLCert, parsedConfig.SSLKey, nil))
|
||||||
|
} else {
|
||||||
|
log.Println("running without SSL enabled")
|
||||||
|
log.Fatalln(http.ListenAndServe(":"+parsedConfig.ListenPort, nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupPgp(config Conf) error {
|
||||||
|
if config.PGPSecretKey == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
secretKey, err := os.Open(config.PGPSecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open private key ring file: %s", err)
|
||||||
|
}
|
||||||
|
defer secretKey.Close()
|
||||||
|
|
||||||
|
entitylist, err := openpgp.ReadKeyRing(secretKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read key ring: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entitylist) < 1 {
|
||||||
|
return errors.New("no keys in key ring")
|
||||||
|
}
|
||||||
|
|
||||||
|
pgpEntity = entitylist[0]
|
||||||
|
|
||||||
|
passphrase := []byte(config.PGPPassphrase)
|
||||||
|
|
||||||
|
if err := pgpEntity.PrivateKey.Decrypt(passphrase); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, subkey := range pgpEntity.Subkeys {
|
||||||
|
err := subkey.PrivateKey.Decrypt(passphrase)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDirs(config Conf) error {
|
||||||
|
for _, distro := range config.DistroNames {
|
||||||
|
for _, arch := range config.SupportArch {
|
||||||
|
if _, err := os.Stat(config.ArchPath(distro, arch)); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
log.Printf("Directory for %s (%s) does not exist, creating", distro, arch)
|
||||||
|
if err := os.MkdirAll(config.ArchPath(distro, arch), 0755); err != nil {
|
||||||
|
return fmt.Errorf("error creating directory for %s (%s): %s", distro, arch, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("error inspecting %s (%s): %s", distro, arch, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(config.PoolPath(distro, arch)); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
log.Printf("Directory for %s (%s) does not exist, creating", distro, arch)
|
||||||
|
if err := os.MkdirAll(config.PoolPath(distro, arch), 0755); err != nil {
|
||||||
|
return fmt.Errorf("error creating directory for %s (%s): %s", distro, arch, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("error inspecting %s (%s): %s", distro, arch, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,535 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var goodOutput = `Package: vim-tiny
|
||||||
|
Source: vim
|
||||||
|
Version: 2:7.4.052-1ubuntu3
|
||||||
|
Architecture: amd64
|
||||||
|
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
|
||||||
|
Installed-Size: 931
|
||||||
|
Depends: vim-common (= 2:7.4.052-1ubuntu3), libacl1 (>= 2.2.51-8), libc6 (>= 2.15), libselinux1 (>= 1.32), libtinfo5
|
||||||
|
Suggests: indent
|
||||||
|
Provides: editor
|
||||||
|
Section: editors
|
||||||
|
Priority: important
|
||||||
|
Homepage: http://www.vim.org/
|
||||||
|
Description: Vi IMproved - enhanced vi editor - compact version
|
||||||
|
Vim is an almost compatible version of the UNIX editor Vi.
|
||||||
|
.
|
||||||
|
Many new features have been added: multi level undo, syntax
|
||||||
|
highlighting, command line history, on-line help, filename
|
||||||
|
completion, block operations, folding, Unicode support, etc.
|
||||||
|
.
|
||||||
|
This package contains a minimal version of vim compiled with no
|
||||||
|
GUI and a small subset of features in order to keep small the
|
||||||
|
package size. This package does not depend on the vim-runtime
|
||||||
|
package, but installing it you will get its additional benefits
|
||||||
|
(online documentation, plugins, ...).
|
||||||
|
Original-Maintainer: Debian Vim Maintainers <pkg-vim-maintainers@lists.alioth.debian.org>
|
||||||
|
`
|
||||||
|
|
||||||
|
var goodPkgGzOutput = `Package: vim-tiny
|
||||||
|
Source: vim
|
||||||
|
Version: 2:7.4.052-1ubuntu3
|
||||||
|
Architecture: amd64
|
||||||
|
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
|
||||||
|
Installed-Size: 931
|
||||||
|
Depends: vim-common (= 2:7.4.052-1ubuntu3), libacl1 (>= 2.2.51-8), libc6 (>= 2.15), libselinux1 (>= 1.32), libtinfo5
|
||||||
|
Suggests: indent
|
||||||
|
Provides: editor
|
||||||
|
Section: editors
|
||||||
|
Priority: important
|
||||||
|
Homepage: http://www.vim.org/
|
||||||
|
Description: Vi IMproved - enhanced vi editor - compact version
|
||||||
|
Vim is an almost compatible version of the UNIX editor Vi.
|
||||||
|
.
|
||||||
|
Many new features have been added: multi level undo, syntax
|
||||||
|
highlighting, command line history, on-line help, filename
|
||||||
|
completion, block operations, folding, Unicode support, etc.
|
||||||
|
.
|
||||||
|
This package contains a minimal version of vim compiled with no
|
||||||
|
GUI and a small subset of features in order to keep small the
|
||||||
|
package size. This package does not depend on the vim-runtime
|
||||||
|
package, but installing it you will get its additional benefits
|
||||||
|
(online documentation, plugins, ...).
|
||||||
|
Original-Maintainer: Debian Vim Maintainers <pkg-vim-maintainers@lists.alioth.debian.org>
|
||||||
|
Filename: dists/stable/main/binary-cats/test.deb
|
||||||
|
Size: 391240
|
||||||
|
MD5sum: 0ec79417129746ff789fcff0976730c5
|
||||||
|
SHA1: b2ac976af80f0f50a8336402d5a29c67a2880b9b
|
||||||
|
SHA256: 9938ec82a8c882ebc2d59b64b0bf2ac01e9cbc5a235be4aa268d4f8484e75eab
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
var goodPkgGzOutputNonDefault = `Package: vim-tiny
|
||||||
|
Source: vim
|
||||||
|
Version: 2:7.4.052-1ubuntu3
|
||||||
|
Architecture: amd64
|
||||||
|
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
|
||||||
|
Installed-Size: 931
|
||||||
|
Depends: vim-common (= 2:7.4.052-1ubuntu3), libacl1 (>= 2.2.51-8), libc6 (>= 2.15), libselinux1 (>= 1.32), libtinfo5
|
||||||
|
Suggests: indent
|
||||||
|
Provides: editor
|
||||||
|
Section: editors
|
||||||
|
Priority: important
|
||||||
|
Homepage: http://www.vim.org/
|
||||||
|
Description: Vi IMproved - enhanced vi editor - compact version
|
||||||
|
Vim is an almost compatible version of the UNIX editor Vi.
|
||||||
|
.
|
||||||
|
Many new features have been added: multi level undo, syntax
|
||||||
|
highlighting, command line history, on-line help, filename
|
||||||
|
completion, block operations, folding, Unicode support, etc.
|
||||||
|
.
|
||||||
|
This package contains a minimal version of vim compiled with no
|
||||||
|
GUI and a small subset of features in order to keep small the
|
||||||
|
package size. This package does not depend on the vim-runtime
|
||||||
|
package, but installing it you will get its additional benefits
|
||||||
|
(online documentation, plugins, ...).
|
||||||
|
Original-Maintainer: Debian Vim Maintainers <pkg-vim-maintainers@lists.alioth.debian.org>
|
||||||
|
Filename: dists/blah/main/binary-cats/test.deb
|
||||||
|
Size: 391240
|
||||||
|
MD5sum: 0ec79417129746ff789fcff0976730c5
|
||||||
|
SHA1: b2ac976af80f0f50a8336402d5a29c67a2880b9b
|
||||||
|
SHA256: 9938ec82a8c882ebc2d59b64b0bf2ac01e9cbc5a235be4aa268d4f8484e75eab
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestCreateDirs(t *testing.T) {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to get current working directory: %s", err)
|
||||||
|
}
|
||||||
|
config := Conf{ListenPort: "9666", RootRepoPath: pwd + "/testing", SupportArch: []string{"cats", "dogs"}, DistroNames: []string{"stable"}, EnableSSL: false}
|
||||||
|
// sanity check...
|
||||||
|
if config.RootRepoPath != pwd+"/testing" {
|
||||||
|
t.Errorf("RootRepoPath is %s, should be %s\n ", config.RootRepoPath, pwd+"/testing")
|
||||||
|
}
|
||||||
|
t.Log("creating temp dirs in ", config.RootRepoPath)
|
||||||
|
if err := createDirs(config); err != nil {
|
||||||
|
t.Errorf("createDirs() failed ")
|
||||||
|
}
|
||||||
|
for _, archDir := range config.SupportArch {
|
||||||
|
if _, err := os.Stat(config.RootRepoPath + "/dists/stable/main/binary-" + archDir); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Errorf("Directory for %s does not exist", archDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(config.RootRepoPath); err != nil {
|
||||||
|
t.Errorf("error cleaning up after createDirs(): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create temp file
|
||||||
|
tempFile, err := os.Create(pwd + "/tempFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create %s: %s", pwd+"/tempFile", err)
|
||||||
|
}
|
||||||
|
defer tempFile.Close()
|
||||||
|
config.RootRepoPath = pwd + "/tempFile"
|
||||||
|
// Can't make directory named after file.
|
||||||
|
if err := createDirs(config); err == nil {
|
||||||
|
t.Errorf("createDirs() should have failed but did not")
|
||||||
|
}
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(pwd + "/tempFile"); err != nil {
|
||||||
|
t.Errorf("error cleaning up after createDirs(): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInspectPackage(t *testing.T) {
|
||||||
|
parsedControl, err := inspectPackage("samples/vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("inspectPackage() error: %s", err)
|
||||||
|
}
|
||||||
|
if parsedControl != goodOutput {
|
||||||
|
t.Errorf("control file does not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = inspectPackage("thisfileshouldnotexist")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("inspectPackage() should have failed, it did not")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInspectPackageControl(t *testing.T) {
|
||||||
|
sampleDeb, err := ioutil.ReadFile("samples/control.tar.gz")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error opening sample deb file: %s", err)
|
||||||
|
}
|
||||||
|
var controlBuf bytes.Buffer
|
||||||
|
cfReader := bytes.NewReader(sampleDeb)
|
||||||
|
io.Copy(&controlBuf, cfReader)
|
||||||
|
parsedControl, err := inspectPackageControl(controlBuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error inspecting control file: %s", err)
|
||||||
|
}
|
||||||
|
if parsedControl != goodOutput {
|
||||||
|
t.Errorf("control file does not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
var failControlBuf bytes.Buffer
|
||||||
|
_, err = inspectPackageControl(failControlBuf)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("inspectPackageControl() should have failed, it did not")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreatePackagesGz(t *testing.T) {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to get current working directory: %s", err)
|
||||||
|
}
|
||||||
|
config := Conf{ListenPort: "9666", RootRepoPath: pwd + "/testing", SupportArch: []string{"cats", "dogs"}, DistroNames: []string{"stable"}, EnableSSL: false}
|
||||||
|
// sanity check...
|
||||||
|
if config.RootRepoPath != pwd+"/testing" {
|
||||||
|
t.Errorf("RootRepoPath is %s, should be %s\n ", config.RootRepoPath, pwd+"/testing")
|
||||||
|
}
|
||||||
|
// copy sample deb to repo location (assuming it exists)
|
||||||
|
origDeb, err := os.Open("samples/vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error opening up sample deb: %s", err)
|
||||||
|
}
|
||||||
|
defer origDeb.Close()
|
||||||
|
for _, archDir := range config.SupportArch {
|
||||||
|
// do not use the built-in createDirs() in case it is broken
|
||||||
|
if err := os.MkdirAll(config.RootRepoPath+"/dists/stable/main/binary-"+archDir, 0755); err != nil {
|
||||||
|
t.Errorf("error creating directory for %s: %s\n", archDir, err)
|
||||||
|
}
|
||||||
|
copyDeb, err := os.Create(config.RootRepoPath + "/dists/stable/main/binary-" + archDir + "/test.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating copy of deb: %s", err)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(copyDeb, origDeb)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error writing copy of deb: %s", err)
|
||||||
|
}
|
||||||
|
if err := copyDeb.Close(); err != nil {
|
||||||
|
t.Errorf("error saving copy of deb: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := createPackagesGz(config, "stable", "cats"); err != nil {
|
||||||
|
t.Errorf("error creating packages gzip for cats")
|
||||||
|
}
|
||||||
|
pkgGzip, err := ioutil.ReadFile(config.RootRepoPath + "/dists/stable/main/binary-cats/Packages.gz")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error reading Packages.gz: %s", err)
|
||||||
|
}
|
||||||
|
pkgReader, err := gzip.NewReader(bytes.NewReader(pkgGzip))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error reading existing Packages.gz: %s", err)
|
||||||
|
}
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
io.Copy(buf, pkgReader)
|
||||||
|
if goodPkgGzOutput != string(buf.Bytes()) {
|
||||||
|
t.Errorf("Packages.gz does not match, returned value is: %s", string(buf.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(config.RootRepoPath); err != nil {
|
||||||
|
t.Errorf("error cleaning up after createPackagesGz(): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create temp file
|
||||||
|
tempFile, err := os.Create(pwd + "/tempFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create %s: %s", pwd+"/tempFile", err)
|
||||||
|
}
|
||||||
|
defer tempFile.Close()
|
||||||
|
config.RootRepoPath = pwd + "/tempFile"
|
||||||
|
// Can't make directory named after file
|
||||||
|
if err := createPackagesGz(config, "stable", "cats"); err == nil {
|
||||||
|
t.Errorf("createPackagesGz() should have failed, it did not")
|
||||||
|
}
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(pwd + "/tempFile"); err != nil {
|
||||||
|
t.Errorf("error cleaning up after createDirs(): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreatePackagesGzNonDefault(t *testing.T) {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to get current working directory: %s", err)
|
||||||
|
}
|
||||||
|
config := Conf{ListenPort: "9666", RootRepoPath: pwd + "/testing", SupportArch: []string{"cats", "dogs"}, DistroNames: []string{"blah"}, EnableSSL: false}
|
||||||
|
// sanity check...
|
||||||
|
if config.RootRepoPath != pwd+"/testing" {
|
||||||
|
t.Errorf("RootRepoPath is %s, should be %s\n ", config.RootRepoPath, pwd+"/testing")
|
||||||
|
}
|
||||||
|
// copy sample deb to repo location (assuming it exists)
|
||||||
|
origDeb, err := os.Open("samples/vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error opening up sample deb: %s", err)
|
||||||
|
}
|
||||||
|
defer origDeb.Close()
|
||||||
|
for _, archDir := range config.SupportArch {
|
||||||
|
// do not use the built-in createDirs() in case it is broken
|
||||||
|
if err := os.MkdirAll(config.RootRepoPath+"/dists/blah/main/binary-"+archDir, 0755); err != nil {
|
||||||
|
t.Errorf("error creating directory for %s: %s\n", archDir, err)
|
||||||
|
}
|
||||||
|
copyDeb, err := os.Create(config.RootRepoPath + "/dists/blah/main/binary-" + archDir + "/test.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating copy of deb: %s", err)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(copyDeb, origDeb)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error writing copy of deb: %s", err)
|
||||||
|
}
|
||||||
|
if err := copyDeb.Close(); err != nil {
|
||||||
|
t.Errorf("error saving copy of deb: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := createPackagesGz(config, "blah", "cats"); err != nil {
|
||||||
|
t.Errorf("error creating packages gzip for cats")
|
||||||
|
}
|
||||||
|
pkgGzip, err := ioutil.ReadFile(config.RootRepoPath + "/dists/blah/main/binary-cats/Packages.gz")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error reading Packages.gz: %s", err)
|
||||||
|
}
|
||||||
|
pkgReader, err := gzip.NewReader(bytes.NewReader(pkgGzip))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error reading existing Packages.gz: %s", err)
|
||||||
|
}
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
io.Copy(buf, pkgReader)
|
||||||
|
if goodPkgGzOutputNonDefault != string(buf.Bytes()) {
|
||||||
|
t.Errorf("Packages.gz does not match, returned value is: %s", string(buf.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(config.RootRepoPath); err != nil {
|
||||||
|
t.Errorf("error cleaning up after createPackagesGz(): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadHandler(t *testing.T) {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to get current working directory: %s", err)
|
||||||
|
}
|
||||||
|
config := Conf{ListenPort: "9666", RootRepoPath: pwd + "/testing", SupportArch: []string{"cats", "dogs"}, DistroNames: []string{"stable"}, EnableSSL: false}
|
||||||
|
// sanity check...
|
||||||
|
if config.RootRepoPath != pwd+"/testing" {
|
||||||
|
t.Errorf("RootRepoPath is %s, should be %s\n ", config.RootRepoPath, pwd+"/testing")
|
||||||
|
}
|
||||||
|
uploadHandle := uploadHandler(config)
|
||||||
|
// GET
|
||||||
|
req, _ := http.NewRequest("GET", "", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
uploadHandle.ServeHTTP(w, req)
|
||||||
|
if w.Code != http.StatusMethodNotAllowed {
|
||||||
|
t.Errorf("uploadHandler GET returned %v, should be %v", w.Code, http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST
|
||||||
|
// create "all" arch as it's the default
|
||||||
|
if err := os.MkdirAll(config.RootRepoPath+"/dists/stable/main/binary-all", 0755); err != nil {
|
||||||
|
t.Error("error creating directory for POST testing: %s", err)
|
||||||
|
}
|
||||||
|
sampleDeb, err := os.Open("samples/vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error opening sample deb file: %s", err)
|
||||||
|
}
|
||||||
|
defer sampleDeb.Close()
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
part, err := writer.CreateFormFile("file", "vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error FormFile: %s", err)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(part, sampleDeb)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error copying sampleDeb to FormFile: %s", err)
|
||||||
|
}
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
t.Errorf("error closing form writer: %s", err)
|
||||||
|
}
|
||||||
|
req, _ = http.NewRequest("POST", "", body)
|
||||||
|
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
uploadHandle.ServeHTTP(w, req)
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Errorf("uploadHandler POST returned %v, should be %v", w.Code, http.StatusOK)
|
||||||
|
}
|
||||||
|
// verify uploaded file matches sample
|
||||||
|
uploadFile, _ := ioutil.ReadFile(config.RootRepoPath + "/dists/stable/main/binary-all/vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
uploadmd5hash := md5.New()
|
||||||
|
uploadmd5hash.Write(uploadFile)
|
||||||
|
uploadFilemd5 := hex.EncodeToString(uploadmd5hash.Sum(nil))
|
||||||
|
|
||||||
|
sampleFile, _ := ioutil.ReadFile("samples/vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
samplemd5hash := md5.New()
|
||||||
|
samplemd5hash.Write(sampleFile)
|
||||||
|
sampleFilemd5 := hex.EncodeToString(samplemd5hash.Sum(nil))
|
||||||
|
if uploadFilemd5 != sampleFilemd5 {
|
||||||
|
t.Errorf("uploaded file MD5 is %s, should be %s", uploadFilemd5, sampleFilemd5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(config.RootRepoPath); err != nil {
|
||||||
|
t.Errorf("error cleaning up after uploadHandler(): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create temp file
|
||||||
|
tempFile, err := os.Create(pwd + "/tempFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create %s: %s", pwd+"/tempFile", err)
|
||||||
|
}
|
||||||
|
defer tempFile.Close()
|
||||||
|
config.RootRepoPath = pwd + "/tempFile"
|
||||||
|
// Can't make directory named after file
|
||||||
|
uploadHandle = uploadHandler(config)
|
||||||
|
failBody := &bytes.Buffer{}
|
||||||
|
failWriter := multipart.NewWriter(failBody)
|
||||||
|
failPart, err := failWriter.CreateFormFile("file", "vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error FormFile: %s", err)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(failPart, sampleDeb)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error copying sampleDeb to FormFile: %s", err)
|
||||||
|
}
|
||||||
|
if err := failWriter.Close(); err != nil {
|
||||||
|
t.Errorf("error closing form writer: %s", err)
|
||||||
|
}
|
||||||
|
req, _ = http.NewRequest("POST", "", failBody)
|
||||||
|
req.Header.Add("Content-Type", failWriter.FormDataContentType())
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
uploadHandle.ServeHTTP(w, req)
|
||||||
|
if w.Code != http.StatusInternalServerError {
|
||||||
|
t.Errorf("uploadHandler POST returned %v, should be %v", w.Code, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(pwd + "/tempFile"); err != nil {
|
||||||
|
t.Errorf("error cleaning up after createDirs(): %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteHandler(t *testing.T) {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to get current working directory: %s", err)
|
||||||
|
}
|
||||||
|
config := Conf{ListenPort: "9666", RootRepoPath: pwd + "/testing", SupportArch: []string{"cats", "dogs"}, DistroNames: []string{"stable"}, EnableSSL: false}
|
||||||
|
// sanity check...
|
||||||
|
if config.RootRepoPath != pwd+"/testing" {
|
||||||
|
t.Errorf("RootRepoPath is %s, should be %s\n ", config.RootRepoPath, pwd+"/testing")
|
||||||
|
}
|
||||||
|
deleteHandle := deleteHandler(config)
|
||||||
|
// GET
|
||||||
|
req, _ := http.NewRequest("GET", "", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
deleteHandle.ServeHTTP(w, req)
|
||||||
|
if w.Code != http.StatusMethodNotAllowed {
|
||||||
|
t.Errorf("deleteHandler GET returned %v, should be %v", w.Code, http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
// create "all" arch as it's the default
|
||||||
|
if err := os.MkdirAll(config.RootRepoPath+"/dists/stable/main/binary-all", 0755); err != nil {
|
||||||
|
t.Error("error creating directory for POST testing: %s", err)
|
||||||
|
}
|
||||||
|
tempDeb, err := os.Create(config.RootRepoPath + "/dists/stable/main/binary-all/myapp.deb")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create %s: %s", config.RootRepoPath+"/dists/stable/main/binary-all/myapp.deb", err)
|
||||||
|
}
|
||||||
|
defer tempDeb.Close()
|
||||||
|
req, _ = http.NewRequest("DELETE", "", bytes.NewBufferString("{\"filename\":\"myapp.deb\",\"arch\":\"all\", \"distroName\":\"stable\"}"))
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
deleteHandle.ServeHTTP(w, req)
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Errorf("deleteHandler DELETE returned %v, should be %v", w.Code, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(config.RootRepoPath); err != nil {
|
||||||
|
t.Errorf("error cleaning up after uploadHandler(): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create temp file
|
||||||
|
tempFile, err := os.Create(pwd + "/tempFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create %s: %s", pwd+"/tempFile", err)
|
||||||
|
}
|
||||||
|
defer tempFile.Close()
|
||||||
|
config.RootRepoPath = pwd + "/tempFile"
|
||||||
|
// Can't make directory named after file
|
||||||
|
deleteHandle = deleteHandler(config)
|
||||||
|
req, _ = http.NewRequest("DELETE", "", bytes.NewBufferString("{\"filename\":\"myapp.deb\",\"arch\":\"amd64\", \"distroName\":\"stable\"}"))
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
deleteHandle.ServeHTTP(w, req)
|
||||||
|
if w.Code != http.StatusInternalServerError {
|
||||||
|
t.Errorf("deleteHandler DELETE returned %v, should be %v", w.Code, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
// cleanup
|
||||||
|
if err := os.RemoveAll(pwd + "/tempFile"); err != nil {
|
||||||
|
t.Errorf("error cleaning up after createDirs(): %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUploadHandler(b *testing.B) {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
b.Errorf("Unable to get current working directory: %s", err)
|
||||||
|
}
|
||||||
|
config := &Conf{ListenPort: "9666", RootRepoPath: pwd + "/testing", SupportArch: []string{"cats", "dogs"}, DistroNames: []string{"stable"}, EnableSSL: false}
|
||||||
|
// sanity check...
|
||||||
|
if config.RootRepoPath != pwd+"/testing" {
|
||||||
|
b.Errorf("RootRepoPath is %s, should be %s\n ", config.RootRepoPath, pwd+"/testing")
|
||||||
|
}
|
||||||
|
uploadHandle := uploadHandler(*config)
|
||||||
|
if err := os.MkdirAll(config.RootRepoPath+"/dists/stable/main/binary-all", 0755); err != nil {
|
||||||
|
b.Errorf("error creating directory for POST testing: %s", err)
|
||||||
|
}
|
||||||
|
sampleDeb, err := os.Open("samples/vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
if err != nil {
|
||||||
|
b.Errorf("error opening sample deb file: %s", err)
|
||||||
|
}
|
||||||
|
defer sampleDeb.Close()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
// temporary (i hope) hack to solve "http: MultipartReader called twice" error
|
||||||
|
b.StopTimer()
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
part, err := writer.CreateFormFile("file", "vim-tiny_7.4.052-1ubuntu3_amd64.deb")
|
||||||
|
if err != nil {
|
||||||
|
b.Errorf("error FormFile: %s", err)
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(part, sampleDeb); err != nil {
|
||||||
|
b.Errorf("error copying sampleDeb to FormFile: %s", err)
|
||||||
|
}
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
b.Errorf("error closing form writer: %s", err)
|
||||||
|
}
|
||||||
|
req, _ := http.NewRequest("POST", "/upload?distro=stable", body)
|
||||||
|
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
b.StartTimer()
|
||||||
|
uploadHandle.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
b.StopTimer()
|
||||||
|
// cleanup
|
||||||
|
_ = os.RemoveAll(config.RootRepoPath)
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
variableRegexp = regexp.MustCompile("(.*?):\\s*(.*)")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Package struct {
|
||||||
|
Package string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePackageData(ctlData string) (*Package, error) {
|
||||||
|
res := &Package{}
|
||||||
|
|
||||||
|
for _, match := range variableRegexp.FindAllStringSubmatch(ctlData, -1) {
|
||||||
|
switch match[1] {
|
||||||
|
case "Package":
|
||||||
|
res.Package = match[2]
|
||||||
|
case "Version":
|
||||||
|
res.Version = match[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"listenPort" : "9090",
|
||||||
|
"rootRepoPath" : "/opt/deb-simple/repo",
|
||||||
|
"supportedArch" : ["all","i386","amd64"],
|
||||||
|
"distroNames":["stable"],
|
||||||
|
"pgpSecretKey": "secring.gpg",
|
||||||
|
"pgpPassphrase" : "",
|
||||||
|
"enableSSL" : false,
|
||||||
|
"SSLcert" : "server.crt",
|
||||||
|
"SSLkey" : "server.key",
|
||||||
|
"key" : "abcdefg"
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue