[
  {
    "path": ".gitignore",
    "content": "data\ncoreos-ipxe-server\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2014 Kelsey Hightower\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# CoreOS iPXE Server\n\n[![Build Status](https://drone.io/github.com/kelseyhightower/coreos-ipxe-server/status.png)](https://drone.io/github.com/kelseyhightower/coreos-ipxe-server/latest)\n\nThe CoreOS iPXE Server attempts to automate as much of the [Booting CoreOS via iPXE](https://coreos.com/docs/running-coreos/bare-metal/booting-with-ipxe/) process as possible, mainly generating iPXE boot scripts and serving CoreOS PXE boot images.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Getting Started](docs/getting_started.md)\n- [API](docs/api.md)\n- [Profiles](docs/profiles.md)\n- [Docker setup example](docs/docker.md)\n\n## Installation\n\n### Binary Release\n\n```\ncurl -L https://github.com/kelseyhightower/coreos-ipxe-server/releases/download/v0.3.0/coreos-ipxe-server-0.3.0-darwin-amd64 -o coreos-ipxe-server\nchmod +x coreos-ipxe-server\n```\n\n### Source\n\n#### Clone\n\n```\nmkdir -p ${GOPATH}/src/github.com/kelseyhightower\ncd ${GOPATH}/src/github.com/kelseyhightower\ngit clone git@github.com:kelseyhightower/coreos-ipxe-server.git\n```\n\n#### Build\n\n```\ncd ${GOPATH}/src/github.com/kelseyhightower/coreos-ipxe-server\ngo build .\n```\n"
  },
  {
    "path": "api.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"path/filepath\"\n\t\"text/template\"\n\n\t\"github.com/kelseyhightower/coreos-ipxe-server/config\"\n\t\"github.com/kelseyhightower/coreos-ipxe-server/kernel\"\n)\n\nconst ipxeBootScript = `#!ipxe\nset coreos-version {{.Version}}\nset base-url http://{{.BaseUrl}}/images/amd64-usr/${coreos-version}\nkernel ${base-url}/coreos_production_pxe.vmlinuz{{.Options}}\ninitrd ${base-url}/coreos_production_pxe_image.cpio.gz\nboot\n`\n\nfunc ipxeBootScriptServer(w http.ResponseWriter, r *http.Request) {\n\tlog.Printf(\"creating boot script for %s\", r.RemoteAddr)\n\n\tbaseUrl := config.BaseUrl\n\tif baseUrl == \"\" {\n\t\tbaseUrl = r.Host\n\t}\n\tv := r.URL.Query()\n\n\toptions := kernel.New()\n\n\t// Process the profile parameter.\n\tprofile := v.Get(\"profile\")\n\tif profile != \"\" {\n\t\tprofilePath := filepath.Join(config.DataDir, fmt.Sprintf(\"profiles/%s.json\", profile))\n\t\terr := kernalOptionsFromFile(profilePath, options)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error reading kernal options from %s: %s\", profilePath, err)\n\t\t\thttp.Error(w, err.Error(), 500)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif options.CloudConfig != \"\" {\n\t\toptions.SetCloudConfigUrl(fmt.Sprintf(\"http://%s/configs/%s.yml\", baseUrl, options.CloudConfig))\n\t}\n\tif options.SSHKey != \"\" {\n\t\tsshKeyPath := filepath.Join(config.DataDir, fmt.Sprintf(\"sshkeys/%s.pub\", options.SSHKey))\n\t\tsshKey, err := sshKeyFromFile(sshKeyPath)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error reading ssh publickey from %s: %s\", sshKeyPath, err)\n\t\t\thttp.Error(w, err.Error(), 500)\n\t\t\treturn\n\t\t}\n\t\toptions.SSHKey = sshKey\n\t}\n\n\t// Process the iPXE boot script template.\n\tt, err := template.New(\"ipxebootscript\").Parse(ipxeBootScript)\n\tif err != nil {\n\t\tlog.Print(\"Error generating iPXE boot script: \" + err.Error())\n\t\thttp.Error(w, \"Error generating the iPXE boot script\", 500)\n\t\treturn\n\t}\n\tdata := map[string]string{\n\t\t\"BaseUrl\": baseUrl,\n\t\t\"Options\": options.String(),\n\t\t\"Version\": options.Version,\n\t}\n\terr = t.Execute(w, data)\n\tif err != nil {\n\t\tlog.Print(\"Error generating iPXE boot script: \" + err.Error())\n\t\thttp.Error(w, \"Error generating the iPXE boot script\", 500)\n\t\treturn\n\t}\n\treturn\n}\n\nfunc sshKeyServer(w http.ResponseWriter, r *http.Request) {\n\tv := r.URL.Query()\n\tkeyName := v.Get(\"name\")\n\tif keyName != \"\" {\n\t\tlog.Printf(\"retrieving ssh key %s.pub for %s\", keyName, r.RemoteAddr)\n\t\tsshKeyPath := filepath.Join(config.DataDir, fmt.Sprintf(\"sshkeys/%s.pub\", keyName))\n\t\tsshKey, err := sshKeyFromFile(sshKeyPath)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error reading ssh publickey from %s: %s\", sshKeyPath, err)\n\t\t\thttp.Error(w, err.Error(), 500)\n\t\t\treturn\n\t\t}\n\t\tdata := []byte(fmt.Sprintf(\"[{\\\"key\\\": \\\"%s\\\"}]\", sshKey))\n\t\tw.Write(data)\n\t}\n\treturn\n}\n\nfunc sshKeyFromFile(filename string) (string, error) {\n\tb, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(bytes.TrimSpace(b)), nil\n}\n\nfunc kernalOptionsFromFile(filename string, options *kernel.Options) error {\n\tdata, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = json.Unmarshal(data, options)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "api_test.go",
    "content": "// Copyright 2014 Kelsey Hightower. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/kelseyhightower/coreos-ipxe-server/config\"\n\t\"github.com/kelseyhightower/coreos-ipxe-server/kernel\"\n)\n\nfunc createTestData(profiles map[string]*kernel.Options, sshKeys map[string]string) (string, error) {\n\td, err := ioutil.TempDir(\"\", \"coreos-ipxe-server\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tsshKeyDir := filepath.Join(d, \"sshkeys\")\n\terr = os.Mkdir(sshKeyDir, 0755)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tfor k, v := range sshKeys {\n\t\tsshKeyPath := filepath.Join(sshKeyDir, fmt.Sprintf(\"%s.pub\", k))\n\t\terr := ioutil.WriteFile(sshKeyPath, []byte(v), 0644)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tprofileDir := filepath.Join(d, \"profiles\")\n\terr = os.Mkdir(profileDir, 0755)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tfor k, v := range profiles {\n\t\tprofilePath := filepath.Join(profileDir, fmt.Sprintf(\"%s.json\", k))\n\t\tdata, err := json.Marshal(v)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\terr = ioutil.WriteFile(profilePath, data, 0644)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn d, nil\n}\n\nvar profileAOut = `#!ipxe\nset coreos-version 310.1.0\nset base-url http://example.com/images/amd64-usr/${coreos-version}\nkernel ${base-url}/coreos_production_pxe.vmlinuz\ninitrd ${base-url}/coreos_production_pxe_image.cpio.gz\nboot\n`\n\nvar profileBOut = `#!ipxe\nset coreos-version 310.1.0\nset base-url http://example.com/images/amd64-usr/${coreos-version}\nkernel ${base-url}/coreos_production_pxe.vmlinuz rootfstype=btrfs console=tty0 console=ttyS0 cloud-config-url=http://example.com/configs/b.yml coreos.autologin=ttyS0 sshkey=\"ssh-rsa AAAAB3Ncoreos\" root=/dev/sda1\ninitrd ${base-url}/coreos_production_pxe_image.cpio.gz\nboot\n`\n\nvar iPxeBootScriptTests = []struct {\n\tname    string\n\tbody    string\n\tcode    int\n\tbaseUrl string\n\turl     string\n}{\n\t{\"a\", profileAOut, 200, \"\", \"http://example.com?profile=a\"},\n\t{\"b\", profileBOut, 200, \"example.com\", \"http://example.com?profile=b\"},\n\t{\"c\", \"\", 500, \"example.com\", \"http://example.com?profile=c\"},\n\t{\"d\", \"\", 500, \"example.com\", \"http://example.com?profile=d\"},\n}\n\nfunc TestIPxeBootScriptServer(t *testing.T) {\n\tsshkeys := map[string]string{\n\t\t\"coreos\": \"ssh-rsa AAAAB3Ncoreos\",\n\t}\n\n\tprofiles := map[string]*kernel.Options{\n\t\t\"a\": &kernel.Options{\n\t\t\tCloudConfig:     \"\",\n\t\t\tConsole:         []string{},\n\t\t\tCoreOSAutologin: \"\",\n\t\t\tRoot:            \"\",\n\t\t\tRootFstype:      \"\",\n\t\t\tSSHKey:          \"\",\n\t\t\tVersion:         \"310.1.0\",\n\t\t},\n\t\t\"b\": &kernel.Options{\n\t\t\tCloudConfig:     \"b\",\n\t\t\tConsole:         []string{\"tty0\", \"ttyS0\"},\n\t\t\tCoreOSAutologin: \"ttyS0\",\n\t\t\tRoot:            \"/dev/sda1\",\n\t\t\tRootFstype:      \"btrfs\",\n\t\t\tSSHKey:          \"coreos\",\n\t\t\tVersion:         \"310.1.0\",\n\t\t},\n\t\t\"c\": &kernel.Options{\n\t\t\tCloudConfig:     \"c\",\n\t\t\tConsole:         []string{\"tty0\", \"ttyS0\"},\n\t\t\tCoreOSAutologin: \"ttyS0\",\n\t\t\tRoot:            \"/dev/sda1\",\n\t\t\tRootFstype:      \"btrfs\",\n\t\t\tSSHKey:          \"imabadkey\",\n\t\t\tVersion:         \"310.1.0\",\n\t\t},\n\t}\n\n\ttestDataDir, err := createTestData(profiles, sshkeys)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer os.RemoveAll(testDataDir)\n\n\tconfig.DataDir = testDataDir\n\tfor _, v := range iPxeBootScriptTests {\n\t\tconfig.BaseUrl = v.baseUrl\n\t\treq, err := http.NewRequest(\"GET\", v.url, nil)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tw := httptest.NewRecorder()\n\t\tipxeBootScriptServer(w, req)\n\t\tif w.Code == 200 && (v.name == \"a\" || v.name == \"b\") {\n\t\t\tif w.Body.String() != v.body {\n\t\t\t\tt.Errorf(\"expected %s\\ngot %s\\n\", v.body, w.Body.String())\n\t\t\t}\n\t\t} else if (v.name == \"c\" || v.name == \"d\") && w.Code != 500 {\n\t\t\tt.Errorf(\"expected %d\\ngot %d\\n\", v.code, w.Code)\n\t\t}\n\t}\n}\n\nvar SSHKeyServerTests = []struct {\n\tname    string\n\tbody    string\n\tcode    int\n\tbaseUrl string\n\turl     string\n}{\n\t{\"a\", `[{\"key\": \"ssh-rsa AAAAB3Ncoreos\"}]`, 200, \"\", \"http://example.com/keys?name=coreos\"},\n\t{\"b\", `[{\"key\": \"ssh-rsa AAAAB3Nfoo\"}]`, 200, \"example.com\", \"http://example.com/keys?name=foo\"},\n\t{\"c\", \"\", 500, \"example.com\", \"http://example.com/keys?name=badkey\"},\n}\n\nfunc TestSSHKeyServer(t *testing.T) {\n\tsshkeys := map[string]string{\n\t\t\"coreos\": \"ssh-rsa AAAAB3Ncoreos\",\n\t\t\"foo\":    \"ssh-rsa AAAAB3Nfoo\",\n\t}\n\n\ttestDataDir, err := createTestData(nil, sshkeys)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer os.RemoveAll(testDataDir)\n\n\tconfig.DataDir = testDataDir\n\tfor _, v := range SSHKeyServerTests {\n\t\tconfig.BaseUrl = v.baseUrl\n\t\treq, err := http.NewRequest(\"GET\", v.url, nil)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tw := httptest.NewRecorder()\n\t\tsshKeyServer(w, req)\n\t\tif w.Code == 200 && (v.name == \"a\" || v.name == \"b\") {\n\t\t\tif w.Body.String() != v.body {\n\t\t\t\tt.Errorf(\"expected %s\\ngot %s\\n\", v.body, w.Body.String())\n\t\t\t}\n\t\t} else if (v.name == \"c\") && w.Code != 500 {\n\t\t\tt.Errorf(\"expected %d\\ngot %d\\n\", v.code, w.Code)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "config/config.go",
    "content": "package config\n\nimport (\n\t\"os\"\n)\n\nvar (\n\tBaseUrl    string\n\tDataDir    string\n\tListenAddr string\n)\n\nvar defaultDataDir = \"/opt/coreos-ipxe-server\"\nvar defaultListenAddr = \"0.0.0.0:4777\"\n\nfunc init() {\n\tBaseUrl = os.Getenv(\"COREOS_IPXE_SERVER_BASE_URL\")\n\t// Set the data directory where the coreos directory containing\n\t// the ssh public key, kernal and boot images.\n\tDataDir = os.Getenv(\"COREOS_IPXE_SERVER_DATA_DIR\")\n\tif DataDir == \"\" {\n\t\tDataDir = defaultDataDir\n\t}\n\tListenAddr = os.Getenv(\"COREOS_IPXE_SERVER_LISTEN_ADDR\")\n\tif ListenAddr == \"\" {\n\t\tListenAddr = defaultListenAddr\n\t}\n}\n"
  },
  {
    "path": "docker/.gitignore",
    "content": "configs/\nimages/\nprofiles/\nsshkeys/\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "FROM google/golang\n\nRUN mkdir -p /gopath/src/github.com/kelseyhightower\n\nWORKDIR /gopath/src/github.com/kelseyhightower\nRUN git clone https://github.com/kelseyhightower/coreos-ipxe-server.git\n\nWORKDIR /gopath/src/github.com/kelseyhightower/coreos-ipxe-server\nRUN go install\n\nRUN mkdir -p /opt/coreos-ipxe-server\nADD configs /opt/coreos-ipxe-server/configs\nADD images /opt/coreos-ipxe-server/images\nADD profiles /opt/coreos-ipxe-server/profiles\nADD sshkeys /opt/coreos-ipxe-server/sshkeys\n\nENV COREOS_IPXE_SERVER_DATA_DIR /opt/coreos-ipxe-server\n# URL has to be substituted with correct value. Can be overwritten during docker run\nENV COREOS_IPXE_SERVER_BASE_URL coreos.ipxe.example.com:4777\nENV COREOS_IPXE_SERVER_LISTEN_ADDR 0.0.0.0:4777\n\nCMD /gopath/bin/coreos-ipxe-server\n"
  },
  {
    "path": "docker/setup_docker.sh",
    "content": "#!/bin/bash\n\n# SETUP ENV\nCOREOS_IPXE_SERVER_BASE_URL=\"<ip/dns record>:4777\"\nCOREOS_IPXE_SERVER_LISTEN_ADDR=\"0.0.0.0:4777\"\nVERSIONS=(\"367.1.0\" \"379.3.0\")\n\n# PREPARE DIRECTORY STRUCTURE\nmkdir -p {configs,images,profiles,sshkeys}\n\n# DOWNLOAD IMAGES\nfor VERSION in \"${VERSIONS[@]}\"\ndo\n\techo \"Downloading files for version ${VERSION}\"\n\tmkdir -p images/amd64-usr/$VERSION\n\twget -nc http://storage.core-os.net/coreos/amd64-usr/$VERSION/coreos_production_pxe_image.cpio.gz -O images/amd64-usr/${VERSION}/coreos_production_pxe_image.cpio.gz\n\twget -nc http://storage.core-os.net/coreos/amd64-usr/$VERSION/coreos_production_pxe.vmlinuz -O images/amd64-usr/${VERSION}/coreos_production_pxe.vmlinuz\ndone\n\n# CUSTOMIZE COREOS SERVER CONFIGURATION\necho \"<ssh-key>\" > sshkeys/coreos.pub\n\n\ncat > configs/development.yml <<EOF\n#cloud-config\n\nssh_authorized_keys:\n  - <ssh-key>\n\ncoreos:\n  etcd:\n      # generate a new token for each unique cluster from https://discovery.etcd.io/new\n      # WARNING: replace each time you 'vagrant destroy'\n      discovery: https://discovery.etcd.io/a1efed8239a47c98c12ce07e2b67f0ed\n      addr: $public_ipv4:4001\n      peer-addr: $public_ipv4:7001\n  units:\n    - name: etcd.service\n      command: start\n    - name: fleet.service\n      command: start\n      runtime: no\n      content: |\n        [Unit]\n        Description=fleet\n\n        [Service]\n        Environment=FLEET_PUBLIC_IP=$public_ipv4\n        ExecStart=/usr/bin/fleet\n    - name: docker-tcp.socket\n      command: start\n      enable: true\n      content: |\n        [Unit]\n        Description=Docker Socket for the API\n\n        [Socket]\n        ListenStream=2375\n        Service=docker.service\n        BindIPv6Only=both\n\n        [Install]\n        WantedBy=sockets.target\nEOF\n\nfor VERSION in \"${VERSIONS[@]}\"\ndo\n\tcat > profiles/development_${VERSION}.json <<EOF\n{\n    \"cloud_config\": \"development\",\n    \"rootfstype\": \"btrfs\",\n    \"sshkey\": \"coreos\",\n    \"version\": \"${VERSION}\"\n}\nEOF\ndone\n\n# BUILD DOCKER\ndocker build -t jveverka/ipxe .\n\n# RUN DOCKER\ndocker run -d -p 4777:4777 -e \"COREOS_IPXE_SERVER_BASE_URL=${COREOS_IPXE_SERVER_BASE_URL}\" -e \"COREOS_IPXE_SERVER_LISTEN_ADDR=${COREOS_IPXE_SERVER_LISTEN_ADDR}\" jveverka/ipxe\n\n"
  },
  {
    "path": "docs/api.md",
    "content": "# API\n\n## iPXE Boot Script\n\nDynamically generate a CoreOS iPXE boot script.\n\n```\nGET http://coreos.ipxe.example.com\n```\n\n**Parameters**\n\nName | Type | Description\n-----|------|------------\nprofile | string | The CoreOS iPXE profile to use.\n\n\n#### Generate Boot Script with a specific profile\n\n```\nGET http://coreos.ipxe.example.com?profile=development\n```\n\n**Response**\n\n```\nHTTP/1.1 200 OK\n```\n\n```\nset coreos-version 310.1.0\nset base-url http://coreos.ipxe.example.com/images/amd64-usr/${coreos-version}\nkernel ${base-url}/coreos_production_pxe.vmlinuz console=tty0 rootfstype=btrfs cloud-config-url=http://coreos.ipxe.example.com/configs/development.yml\ninitrd ${base-url}/coreos_production_pxe_image.cpio.gz\nboot\n```\n\n## Retrieve an SSH Public Key\n\nProduce an SSH public key suitable for use by `coreos-cloudconfig` to insert into\na user's `authorized_keys`.\n\n```\nGET http://coreos.ipxe.example.com/keys\n```\n\n**Parameters**\n\nName | Type | Description\n-----|------|------------\nname | string | The name of the key (not including .pub)\n\n\n#### Generate a key for a specified name\n\n```\nGET http://coreos.ipxe.example.com/keys?name=foo\n```\n\n**Response**\n\n```\nHTTP/1.1 200 OK\n```\n\n```\n[{\"key\":\"ssh-rsa AAAAB3N... foo@bar\"}]\n```\n"
  },
  {
    "path": "docs/cloudconfigs.md",
    "content": "### Cloud Configs\n\nCloud configs can be used to automate the configuration of your CoreOS install. The `cloud-config-url` is configured via the cloud-config-url boot parameter, which is part of the CoreOS iPXE boot script. Cloud configs are identified by id and are stored under the `$COREOS_IPXE_SERVER_DATA_DIR/configs` directory.\n\nExample:\n\n```\n$COREOS_IPXE_SERVER_DATA_DIR/configs/development.yml\n```\n"
  },
  {
    "path": "docs/configuration.md",
    "content": "## Configuration\n\n### Environment Variables\n\n#### Optional:\n\n```\nCOREOS_IPXE_SERVER_DATA_DIR\nCOREOS_IPXE_SERVER_BASE_URL\nCOREOS_IPXE_SERVER_LISTEN_ADDR\n```\n\n#### Example:\n\n```\nexport COREOS_IPXE_SERVER_DATA_DIR=\"/opt/coreos-ipxe-server\"\nexport COREOS_IPXE_SERVER_BASE_URL=\"coreos.ipxe.example.com:4777\"\nexport COREOS_IPXE_SERVER_LISTEN_ADDR=\"0.0.0.0:4777\"\n```\n"
  },
  {
    "path": "docs/docker.md",
    "content": "# Example of running coreos-ipxe-server on docker\n\n### The script will\n- Prepare directory structure\n- download images for versions specified in VERSIONS\n- create default config files\n- build ipxe docker image\n- run ipxe docker container\n\n### Following has to be done before running the script\n- update following env vars so that they suit your needs\n - ```\nCOREOS_IPXE_SERVER_BASE_URL ip/dns on which your docker will listen\nCOREOS_IPXE_SERVER_LISTEN_ADDR=\"0.0.0.0:4777\"\nVERSIONS=(\"367.1.0\" \"379.3.0\")\n```\n- include your ssh-key\n- run script\n - cd docker && ./setup_docker.sh\n\nIf everything runs sucessfully you should be able to see you container running among others using ``docker ps``\n\nYou can try to access iPXE config on ``<COREOS_IPXE_SERVER_BASE_URL>:4777/?profile=development_<VERSION>``\nIn my case it will be ``http://10.102.11.42:4777/?profile=development_367.1.0``\n"
  },
  {
    "path": "docs/getting_started.md",
    "content": "# Getting Started\n\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Create the Data Directory](#create-the-data-directory) \n- [Download the CoreOS PXE Images](#download-the-coreos-pxe-images)\n- [Add a Cloud Config File](#add-a-cloud-config-file)\n- [Add a SSH Public Key](#add-an-ssh-public-key)\n- [Add an iPXE Profile](#add-an-ipxe-profile)\n- [Example Data Directory Layout](#example-data-directory-layout)\n\n### Installation\n\n```\ncurl -L https://github.com/kelseyhightower/coreos-ipxe-server/releases/download/v0.3.0/coreos-ipxe-server-0.3.0-darwin-amd64 -o coreos-ipxe-server\nchmod +x coreos-ipxe-server\n```\n\n### Configuration\n\nAll configuration is handled via environment variables with sane defaults. See [Configuration](configuration.md) for more details.\n\n\n### Create the Data Directory\n\nThe data directory is where the CoreOS images, SSH public keys, cloud configs and iPXE profiles are stored. The data directory defaults to `/opt/coreos-ipxe-server`; set it to a different directory via the `COREOS_IPXE_SERVER_DATA_DIR` environment variable:\n\n```\nmkdir -p $COREOS_IPXE_SERVER_DATA_DIR/{configs,images,profiles,sshkeys}\n```\n\n### Download the CoreOS PXE Images\n\nThe CoreOS PXE images are stored under the `$COREOS_IPXE_SERVER_DATA_DIR/images` directory.\n\n```\nmkdir -p $COREOS_IPXE_SERVER_DATA_DIR/images/amd64-usr/310.1.0\ncd $COREOS_IPXE_SERVER_DATA_DIR/images/amd64-usr/310.1.0\nwget http://storage.core-os.net/coreos/amd64-usr/310.1.0/coreos_production_pxe_image.cpio.gz\nwget http://storage.core-os.net/coreos/amd64-usr/310.1.0/coreos_production_pxe.vmlinuz\n```\n\n### Add an SSH Public Key\n\nSSH public keys are used to login to your CoreOS instance. SSH public keys are stored under the `$COREOS_IPXE_SERVER_DATA_DIR/sshkeys` directory.\n\nEdit `$COREOS_IPXE_SERVER_DATA_DIR/sshkeys/coreos.pub`\n\n```\nssh-rsa AAAAB3Nza...\n```\n\n### Add a Cloud Config File\n\nCloud config files are used to automated the setup of your CoreOS instance. See [Customize with Cloud Config](https://coreos.com/docs/cluster-management/setup/cloudinit-cloud-config/) for more details. Cloud config files are stored under the `$COREOS_IPXE_SERVER_DATA_DIR/configs` directory.\n\nEdit `$COREOS_IPXE_SERVER_DATA_DIR/configs/development.yml`\n\n```\n#cloud-config\n\nssh_authorized_keys:\n    - ssh-rsa AAAAB3Nza...\ncoreos:\n  etcd:\n    addr: $private_ipv4:4001\n    peer-addr: $private_ipv4:7001\n  units:\n    - name: etcd.service\n      command: start\n    - name: fleet.service\n      command: start\n    - name: docker.socket\n      command: start\n  oem:\n    id: coreos\n    name: CoreOS Custom\n    version-id: 310.1.0\n    home-url: https://coreos.com\n```\n\n### Add an iPXE Profile\n\niPXE profiles are used to define CoreOS boot parameters. iPXE profiles are stored under the `$COREOS_IPXE_SERVER_DATA_DIR/profiles` directory.\n\nEdit `$COREOS_IPXE_SERVER_DATA_DIR/profiles/development.json` \n\n```\n{\n\t\"cloud_config\": \"development\",\n\t\"rootfstype\": \"btrfs\",\n\t\"sshkey\": \"coreos\",\n\t\"version\": \"310.1.0\"\n}\n```\n\n### Example Data Directory Layout\n\n```\n/opt/coreos-ipxe-server/\n├── configs\n│   └── development.yml\n├── images\n│   └── amd64-usr\n│       └── 310.1.0\n│           ├── coreos_production_pxe.vmlinuz\n│           └── coreos_production_pxe_image.cpio.gz\n├── profiles\n│   └── development.json\n└── sshkeys\n    └── coreos.pub\n```\n"
  },
  {
    "path": "docs/profiles.md",
    "content": "# Profiles\n\niPXE profiles are used to define CoreOS kernel options used during the PXE boot process. Profiles are identified by id and are stored under the `$COREOS_IPXE_SERVER_DATA_DIR/profiles` directory.\n\n## File Format\n\nThe iPXE profile file uses the JSON file format.\n\nA iPXE profile file should contain an associative array which has zero or more of the following keys:\n\n* cloud_config\n* console\n* coreos_autologin\n* rootfstype\n* sshkey\n* version\n\nSee [Configuring pxelinux](https://coreos.com/docs/running-coreos/bare-metal/booting-with-pxe/#configuring-pxelinux) for more details.\n\n### Example Profile\n\n```\n$COREOS_IPXE_SERVER_DATA_DIR/profiles/development.json\n```\n\n```\n{\n  \"cloud_config\": \"development\",\n  \"console\": [\"tty0\", \"tty1\"],\n  \"coreos_autologin\": \"tty1\",\n  \"rootfstype\": \"btrfs\",\n  \"sshkey\": \"coreos\",\n  \"version\": \"310.1.0\"\n}\n```\n"
  },
  {
    "path": "docs/sshkeys.md",
    "content": "# SSH Public Keys\n\nSSH keys are configured via the sshkey boot parameter, which is part of the CoreOS iPXE boot script. SSH keys are identified by id and are stored under the `$COREOS_IPXE_SERVER_DATA_DIR/sshkeys` directory.\n\nExample:\n\n```\n$COREOS_IPXE_SERVER_DATA_DIR/sshkeys/coreos.pub\n```\n"
  },
  {
    "path": "kernel/options.go",
    "content": "package kernel\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n)\n\ntype Options struct {\n\tCloudConfig     string   `json:\"cloud_config\"`\n\tConsole         []string `json:\"console\"`\n\tCoreOSAutologin string   `json:\"coreos_autologin\"`\n\tRoot            string   `json:\"root\"`\n\tRootFstype      string   `json:\"rootfstype\"`\n\tSSHKey          string   `json:\"sshkey\"`\n\tVersion         string   `json:\"version\"`\n\tcloudConfigUrl  string\n}\n\nfunc New() *Options {\n\treturn &Options{}\n}\n\nfunc (o *Options) SetCloudConfigUrl(url string) {\n\to.cloudConfigUrl = url\n}\n\nfunc (o *Options) String() string {\n\tvar options bytes.Buffer\n\tif o.RootFstype != \"\" {\n\t\toptions.WriteString(fmt.Sprintf(\" rootfstype=%s\", o.RootFstype))\n\t}\n\tfor _, c := range o.Console {\n\t\toptions.WriteString(fmt.Sprintf(\" console=%s\", c))\n\t}\n\tif o.cloudConfigUrl != \"\" {\n\t\toptions.WriteString(fmt.Sprintf(\" cloud-config-url=%s\", o.cloudConfigUrl))\n\t}\n\tif o.CoreOSAutologin != \"\" {\n\t\toptions.WriteString(fmt.Sprintf(\" coreos.autologin=%s\", o.CoreOSAutologin))\n\t}\n\tif o.SSHKey != \"\" {\n\t\toptions.WriteString(fmt.Sprintf(\" sshkey=\\\"%s\\\"\", o.SSHKey))\n\t}\n\tif o.Root != \"\" {\n\t\toptions.WriteString(fmt.Sprintf(\" root=%s\", o.Root))\n\t}\n\treturn options.String()\n}\n"
  },
  {
    "path": "kernel/options_test.go",
    "content": "package kernel\n\nimport (\n\t\"testing\"\n)\n\nfunc TestDefaultOptions(t *testing.T) {\n\twant := \"\"\n\to := New()\n\toptions := o.String()\n\tif options != want {\n\t\tt.Errorf(\"wanted %s, got %s\", want, options)\n\t}\n}\n\nvar optionstests = []struct {\n\tcloudConfigUrl  string\n\tconsole         []string\n\tcoreOSAutologin string\n\troot            string\n\trootFstype      string\n\tsshKey          string\n\toptions         string\n}{\n\t{\n\t\t\"http://host/config.yml\",\n\t\t[]string{\"tty0\", \"ttyS0\"},\n\t\t\"ttyS0\",\n\t\t\"\",\n\t\t\"tmpfs\",\n\t\t\"ssh-rsa AAAAB3Nza...\",\n\t\t\" rootfstype=tmpfs console=tty0 console=ttyS0 cloud-config-url=http://host/config.yml coreos.autologin=ttyS0 sshkey=\\\"ssh-rsa AAAAB3Nza...\\\"\",\n\t},\n\t{\n\t\t\"\",\n\t\tnil,\n\t\t\"\",\n\t\t\"\",\n\t\t\"\",\n\t\t\"ssh-rsa AAAAB3Nza...\",\n\t\t\" sshkey=\\\"ssh-rsa AAAAB3Nza...\\\"\",\n\t},\n}\n\nfunc TestOptions(t *testing.T) {\n\tfor _, tt := range optionstests {\n\t\to := New()\n\t\to.SetCloudConfigUrl(tt.cloudConfigUrl)\n\t\tif tt.console != nil {\n\t\t\to.Console = tt.console\n\t\t}\n\t\to.RootFstype = tt.rootFstype\n\t\to.SSHKey = tt.sshKey\n\t\to.CoreOSAutologin = tt.coreOSAutologin\n\t\to.Root = tt.root\n\t\tgot := o.String()\n\t\tif got != tt.options {\n\t\t\tt.Errorf(\"wanted %s, got %s\", tt.options, got)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "main.go",
    "content": "// Copyright 2014 Kelsey Hightower. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"path/filepath\"\n\n\t\"github.com/kelseyhightower/coreos-ipxe-server/config\"\n)\n\nfunc main() {\n\tfor _, s := range []string{\"/images/\", \"/configs/\", \"/profiles/\"} {\n\t\t// Register static file servers.\n\t\thttp.Handle(s, http.StripPrefix(s,\n\t\t\thttp.FileServer(http.Dir(filepath.Join(config.DataDir, s)))))\n\t}\n\t// Register the sshkey script server.\n\thttp.HandleFunc(\"/keys\", sshKeyServer)\n\t// Register the iPXE boot script server.\n\thttp.HandleFunc(\"/\", ipxeBootScriptServer)\n\t// Start the iPXE Boot Server.\n\tfmt.Println(\"Starting CoreOS iPXE Server...\")\n\tfmt.Printf(\"Listening on %s\\n\", config.ListenAddr)\n\tif config.BaseUrl != \"\" {\n\t\tfmt.Printf(\"Advertised URL %s\\n\", config.BaseUrl)\n\t}\n\tfmt.Printf(\"Data directory: %s\\n\", config.DataDir)\n\tlog.Fatal(http.ListenAndServe(config.ListenAddr, nil))\n}\n"
  }
]