fedostree: Rename to "fatomic"
This commit is contained in:
parent
f14d5ab37c
commit
87aed0fa7c
15
fatomic/fedora-20.repo
Normal file
15
fatomic/fedora-20.repo
Normal file
@ -0,0 +1,15 @@
|
||||
[fedora-20]
|
||||
name=Fedora 20 $basearch
|
||||
#baseurl=http://mirror.pnl.gov/fedora/linux/releases/20/Everything/x86_64/os/
|
||||
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-20&arch=$basearch
|
||||
enabled=0
|
||||
gpgcheck=0
|
||||
metadata_expire=7d
|
||||
|
||||
[fedora-20-updates]
|
||||
name=Fedora 20 updates $basearch
|
||||
#baseurl=http://mirror.pnl.gov/fedora/linux/updates/20/x86_64/
|
||||
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-f20&arch=$basearch
|
||||
enabled=0
|
||||
gpgcheck=0
|
||||
metadata_expire=7d
|
6
fatomic/fedora-rawhide.repo
Normal file
6
fatomic/fedora-rawhide.repo
Normal file
@ -0,0 +1,6 @@
|
||||
[fedora-rawhide]
|
||||
name=Fedora rawhide $basearch
|
||||
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-rawhide&arch=$basearch
|
||||
enabled=0
|
||||
gpgcheck=0
|
||||
metadata_expire=7d
|
57
fatomic/products.json
Normal file
57
fatomic/products.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"comment": "Default/demo trees generated by the Fedora Atomic Initiative",
|
||||
|
||||
"osname": "fatomic",
|
||||
"repo": "http://rpm-ostree.cloud.fedoraproject.org/repo",
|
||||
|
||||
"gpg_key": "843833DF",
|
||||
|
||||
"architectures": ["x86_64"],
|
||||
|
||||
"releases": ["rawhide"],
|
||||
|
||||
"repos": ["fedora-rawhide", "walters-rpm-ostree",
|
||||
"fedora-rawhide-kernel-nodebug"],
|
||||
|
||||
"selinux": true,
|
||||
|
||||
"image_formats": ["qcow2"],
|
||||
|
||||
"kargs": ["quiet"],
|
||||
|
||||
"bootstrap_packages": ["filesystem", "glibc", "nss-altfiles", "shadow-utils"],
|
||||
|
||||
"packages": ["kernel", "ostree", "generic-release", "lvm2",
|
||||
"btrfs-progs", "e2fsprogs", "xfsprogs",
|
||||
"rpm-ostree-public-gpg-key", "gnupg2"],
|
||||
|
||||
"postprocess": ["remove-root-password"],
|
||||
|
||||
"products":
|
||||
{
|
||||
"server":
|
||||
{
|
||||
"docker-io": { "comment": "A system to just run Docker",
|
||||
"packages": ["@core", "@standard", "NetworkManager-config-server", "docker-io"],
|
||||
"units": ["docker.service"]
|
||||
},
|
||||
"virthost": { "comment": "A system to just run libvirt",
|
||||
"packages": ["@core", "@standard", "NetworkManager-config-server",
|
||||
"libvirt-daemon-kvm",
|
||||
"libvirt-daemon-config-network",
|
||||
"qemu-kvm",
|
||||
"libguestfs-tools"] }
|
||||
},
|
||||
"workstation":
|
||||
{
|
||||
"gnome/core": { "comment": "The GNOME desktop core",
|
||||
"packages": ["@core", "@standard", "@base-x", "@gnome-desktop"] },
|
||||
"gnome/development-virt": { "comment": "The GNOME desktop with Firefox, games, LibreOffice, virtualization and development tools",
|
||||
"packages": ["@core", "@standard", "@base-x", "@gnome-desktop", "@firefox",
|
||||
"@gnome-games", "@libreoffice", "@virtualization", "@development-tools"] },
|
||||
"kde/core": { "comment": "The KDE desktop core",
|
||||
"packages": ["@core", "@standard", "@base-x", "@kde-desktop"] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
fatomic/walters-rpm-ostree.repo
Normal file
7
fatomic/walters-rpm-ostree.repo
Normal file
@ -0,0 +1,7 @@
|
||||
[walters-rpm-ostree]
|
||||
name=Copr repo for rpm-ostree owned by walters
|
||||
description=RPM OSTree integration
|
||||
baseurl=http://copr-be.cloud.fedoraproject.org/results/walters/rpm-ostree/fedora-20-$basearch/
|
||||
skip_if_unavailable=True
|
||||
gpgcheck=0
|
||||
enabled=1
|
911
fatomic/web/angular-route.js
vendored
Normal file
911
fatomic/web/angular-route.js
vendored
Normal file
@ -0,0 +1,911 @@
|
||||
/**
|
||||
* @license AngularJS v1.2.8
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngRoute
|
||||
* @description
|
||||
*
|
||||
* # ngRoute
|
||||
*
|
||||
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
|
||||
*
|
||||
* ## Example
|
||||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
|
||||
*
|
||||
* {@installModule route}
|
||||
*
|
||||
* <div doc-module-components="ngRoute"></div>
|
||||
*/
|
||||
/* global -ngRouteModule */
|
||||
var ngRouteModule = angular.module('ngRoute', ['ng']).
|
||||
provider('$route', $RouteProvider);
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngRoute.$routeProvider
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Used for configuring routes.
|
||||
*
|
||||
* ## Example
|
||||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*/
|
||||
function $RouteProvider(){
|
||||
function inherit(parent, extra) {
|
||||
return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
|
||||
}
|
||||
|
||||
var routes = {};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngRoute.$routeProvider#when
|
||||
* @methodOf ngRoute.$routeProvider
|
||||
*
|
||||
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
|
||||
* contains redundant trailing slash or is missing one, the route will still match and the
|
||||
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
|
||||
* route definition.
|
||||
*
|
||||
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
|
||||
* to the next slash are matched and stored in `$routeParams` under the given `name`
|
||||
* when the route matches.
|
||||
* * `path` can contain named groups starting with a colon and ending with a star:
|
||||
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
|
||||
* when the route matches.
|
||||
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
|
||||
*
|
||||
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
|
||||
* `/color/brown/largecode/code/with/slashs/edit` and extract:
|
||||
*
|
||||
* * `color: brown`
|
||||
* * `largecode: code/with/slashs`.
|
||||
*
|
||||
*
|
||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||
* match.
|
||||
*
|
||||
* Object properties:
|
||||
*
|
||||
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
|
||||
* newly created scope or the name of a {@link angular.Module#controller registered
|
||||
* controller} if passed as a string.
|
||||
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
|
||||
* published to scope under the `controllerAs` name.
|
||||
* - `template` – `{string=|function()=}` – html template as a string or a function that
|
||||
* returns an html template as a string which should be used by {@link
|
||||
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
|
||||
* This property takes precedence over `templateUrl`.
|
||||
*
|
||||
* If `template` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route
|
||||
*
|
||||
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
|
||||
* template that should be used by {@link ngRoute.directive:ngView ngView}.
|
||||
*
|
||||
* If `templateUrl` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route
|
||||
*
|
||||
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
|
||||
* be injected into the controller. If any of these dependencies are promises, the router
|
||||
* will wait for them all to be resolved or one to be rejected before the controller is
|
||||
* instantiated.
|
||||
* If all the promises are resolved successfully, the values of the resolved promises are
|
||||
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
|
||||
* fired. If any of the promises are rejected the
|
||||
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
|
||||
* is:
|
||||
*
|
||||
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
|
||||
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
|
||||
* Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
|
||||
* and the return value is treated as the dependency. If the result is a promise, it is
|
||||
* resolved before its value is injected into the controller. Be aware that
|
||||
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
|
||||
* functions. Use `$route.current.params` to access the new route parameters, instead.
|
||||
*
|
||||
* - `redirectTo` – {(string|function())=} – value to update
|
||||
* {@link ng.$location $location} path with and trigger route redirection.
|
||||
*
|
||||
* If `redirectTo` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Object.<string>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route templateUrl.
|
||||
* - `{string}` - current `$location.path()`
|
||||
* - `{Object}` - current `$location.search()`
|
||||
*
|
||||
* The custom `redirectTo` function is expected to return a string which will be used
|
||||
* to update `$location.path()` and `$location.search()`.
|
||||
*
|
||||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
|
||||
* or `$location.hash()` changes.
|
||||
*
|
||||
* If the option is set to `false` and url in the browser changes, then
|
||||
* `$routeUpdate` event is broadcasted on the root scope.
|
||||
*
|
||||
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
|
||||
*
|
||||
* If the option is set to `true`, then the particular route can be matched without being
|
||||
* case sensitive
|
||||
*
|
||||
* @returns {Object} self
|
||||
*
|
||||
* @description
|
||||
* Adds a new route definition to the `$route` service.
|
||||
*/
|
||||
this.when = function(path, route) {
|
||||
routes[path] = angular.extend(
|
||||
{reloadOnSearch: true},
|
||||
route,
|
||||
path && pathRegExp(path, route)
|
||||
);
|
||||
|
||||
// create redirection for trailing slashes
|
||||
if (path) {
|
||||
var redirectPath = (path[path.length-1] == '/')
|
||||
? path.substr(0, path.length-1)
|
||||
: path +'/';
|
||||
|
||||
routes[redirectPath] = angular.extend(
|
||||
{redirectTo: path},
|
||||
pathRegExp(redirectPath, route)
|
||||
);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param path {string} path
|
||||
* @param opts {Object} options
|
||||
* @return {?Object}
|
||||
*
|
||||
* @description
|
||||
* Normalizes the given path, returning a regular expression
|
||||
* and the original path.
|
||||
*
|
||||
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
|
||||
*/
|
||||
function pathRegExp(path, opts) {
|
||||
var insensitive = opts.caseInsensitiveMatch,
|
||||
ret = {
|
||||
originalPath: path,
|
||||
regexp: path
|
||||
},
|
||||
keys = ret.keys = [];
|
||||
|
||||
path = path
|
||||
.replace(/([().])/g, '\\$1')
|
||||
.replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
|
||||
var optional = option === '?' ? option : null;
|
||||
var star = option === '*' ? option : null;
|
||||
keys.push({ name: key, optional: !!optional });
|
||||
slash = slash || '';
|
||||
return ''
|
||||
+ (optional ? '' : slash)
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (star && '(.+?)' || '([^/]+)')
|
||||
+ (optional || '')
|
||||
+ ')'
|
||||
+ (optional || '');
|
||||
})
|
||||
.replace(/([\/$\*])/g, '\\$1');
|
||||
|
||||
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngRoute.$routeProvider#otherwise
|
||||
* @methodOf ngRoute.$routeProvider
|
||||
*
|
||||
* @description
|
||||
* Sets route definition that will be used on route change when no other route definition
|
||||
* is matched.
|
||||
*
|
||||
* @param {Object} params Mapping information to be assigned to `$route.current`.
|
||||
* @returns {Object} self
|
||||
*/
|
||||
this.otherwise = function(params) {
|
||||
this.when(null, params);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
this.$get = ['$rootScope',
|
||||
'$location',
|
||||
'$routeParams',
|
||||
'$q',
|
||||
'$injector',
|
||||
'$http',
|
||||
'$templateCache',
|
||||
'$sce',
|
||||
function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngRoute.$route
|
||||
* @requires $location
|
||||
* @requires $routeParams
|
||||
*
|
||||
* @property {Object} current Reference to the current route definition.
|
||||
* The route definition contains:
|
||||
*
|
||||
* - `controller`: The controller constructor as define in route definition.
|
||||
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
|
||||
* controller instantiation. The `locals` contain
|
||||
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
|
||||
*
|
||||
* - `$scope` - The current route scope.
|
||||
* - `$template` - The current route template HTML.
|
||||
*
|
||||
* @property {Array.<Object>} routes Array of all configured routes.
|
||||
*
|
||||
* @description
|
||||
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
|
||||
* It watches `$location.url()` and tries to map the path to an existing route definition.
|
||||
*
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*
|
||||
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
|
||||
*
|
||||
* The `$route` service is typically used in conjunction with the
|
||||
* {@link ngRoute.directive:ngView `ngView`} directive and the
|
||||
* {@link ngRoute.$routeParams `$routeParams`} service.
|
||||
*
|
||||
* @example
|
||||
This example shows how changing the URL hash causes the `$route` to match a route against the
|
||||
URL, and the `ngView` pulls in the partial.
|
||||
|
||||
Note that this example is using {@link ng.directive:script inlined templates}
|
||||
to get it working on jsfiddle as well.
|
||||
|
||||
<example module="ngViewExample" deps="angular-route.js">
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainCntl">
|
||||
Choose:
|
||||
<a href="Book/Moby">Moby</a> |
|
||||
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
||||
<a href="Book/Gatsby">Gatsby</a> |
|
||||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
||||
<a href="Book/Scarlet">Scarlet Letter</a><br/>
|
||||
|
||||
<div ng-view></div>
|
||||
<hr />
|
||||
|
||||
<pre>$location.path() = {{$location.path()}}</pre>
|
||||
<pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
|
||||
<pre>$route.current.params = {{$route.current.params}}</pre>
|
||||
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
|
||||
<pre>$routeParams = {{$routeParams}}</pre>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="book.html">
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
</file>
|
||||
|
||||
<file name="chapter.html">
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
Chapter Id: {{params.chapterId}}
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('ngViewExample', ['ngRoute'])
|
||||
|
||||
.config(function($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/Book/:bookId', {
|
||||
templateUrl: 'book.html',
|
||||
controller: BookCntl,
|
||||
resolve: {
|
||||
// I will cause a 1 second delay
|
||||
delay: function($q, $timeout) {
|
||||
var delay = $q.defer();
|
||||
$timeout(delay.resolve, 1000);
|
||||
return delay.promise;
|
||||
}
|
||||
}
|
||||
});
|
||||
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
|
||||
templateUrl: 'chapter.html',
|
||||
controller: ChapterCntl
|
||||
});
|
||||
|
||||
// configure html5 to get links working on jsfiddle
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
||||
function MainCntl($scope, $route, $routeParams, $location) {
|
||||
$scope.$route = $route;
|
||||
$scope.$location = $location;
|
||||
$scope.$routeParams = $routeParams;
|
||||
}
|
||||
|
||||
function BookCntl($scope, $routeParams) {
|
||||
$scope.name = "BookCntl";
|
||||
$scope.params = $routeParams;
|
||||
}
|
||||
|
||||
function ChapterCntl($scope, $routeParams) {
|
||||
$scope.name = "ChapterCntl";
|
||||
$scope.params = $routeParams;
|
||||
}
|
||||
</file>
|
||||
|
||||
<file name="scenario.js">
|
||||
it('should load and compile correct template', function() {
|
||||
element('a:contains("Moby: Ch1")').click();
|
||||
var content = element('.doc-example-live [ng-view]').text();
|
||||
expect(content).toMatch(/controller\: ChapterCntl/);
|
||||
expect(content).toMatch(/Book Id\: Moby/);
|
||||
expect(content).toMatch(/Chapter Id\: 1/);
|
||||
|
||||
element('a:contains("Scarlet")').click();
|
||||
sleep(2); // promises are not part of scenario waiting
|
||||
content = element('.doc-example-live [ng-view]').text();
|
||||
expect(content).toMatch(/controller\: BookCntl/);
|
||||
expect(content).toMatch(/Book Id\: Scarlet/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.$route#$routeChangeStart
|
||||
* @eventOf ngRoute.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted before a route change. At this point the route services starts
|
||||
* resolving all of the dependencies needed for the route change to occurs.
|
||||
* Typically this involves fetching the view template as well as any dependencies
|
||||
* defined in `resolve` route property. Once all of the dependencies are resolved
|
||||
* `$routeChangeSuccess` is fired.
|
||||
*
|
||||
* @param {Object} angularEvent Synthetic event object.
|
||||
* @param {Route} next Future route information.
|
||||
* @param {Route} current Current route information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.$route#$routeChangeSuccess
|
||||
* @eventOf ngRoute.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted after a route dependencies are resolved.
|
||||
* {@link ngRoute.directive:ngView ngView} listens for the directive
|
||||
* to instantiate the controller and render the view.
|
||||
*
|
||||
* @param {Object} angularEvent Synthetic event object.
|
||||
* @param {Route} current Current route information.
|
||||
* @param {Route|Undefined} previous Previous route information, or undefined if current is
|
||||
* first route entered.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.$route#$routeChangeError
|
||||
* @eventOf ngRoute.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted if any of the resolve promises are rejected.
|
||||
*
|
||||
* @param {Object} angularEvent Synthetic event object
|
||||
* @param {Route} current Current route information.
|
||||
* @param {Route} previous Previous route information.
|
||||
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.$route#$routeUpdate
|
||||
* @eventOf ngRoute.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
*
|
||||
* The `reloadOnSearch` property has been set to false, and we are reusing the same
|
||||
* instance of the Controller.
|
||||
*/
|
||||
|
||||
var forceReload = false,
|
||||
$route = {
|
||||
routes: routes,
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngRoute.$route#reload
|
||||
* @methodOf ngRoute.$route
|
||||
*
|
||||
* @description
|
||||
* Causes `$route` service to reload the current route even if
|
||||
* {@link ng.$location $location} hasn't changed.
|
||||
*
|
||||
* As a result of that, {@link ngRoute.directive:ngView ngView}
|
||||
* creates new scope, reinstantiates the controller.
|
||||
*/
|
||||
reload: function() {
|
||||
forceReload = true;
|
||||
$rootScope.$evalAsync(updateRoute);
|
||||
}
|
||||
};
|
||||
|
||||
$rootScope.$on('$locationChangeSuccess', updateRoute);
|
||||
|
||||
return $route;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @param on {string} current url
|
||||
* @param route {Object} route regexp to match the url against
|
||||
* @return {?Object}
|
||||
*
|
||||
* @description
|
||||
* Check if the route matches the current url.
|
||||
*
|
||||
* Inspired by match in
|
||||
* visionmedia/express/lib/router/router.js.
|
||||
*/
|
||||
function switchRouteMatcher(on, route) {
|
||||
var keys = route.keys,
|
||||
params = {};
|
||||
|
||||
if (!route.regexp) return null;
|
||||
|
||||
var m = route.regexp.exec(on);
|
||||
if (!m) return null;
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
var key = keys[i - 1];
|
||||
|
||||
var val = 'string' == typeof m[i]
|
||||
? decodeURIComponent(m[i])
|
||||
: m[i];
|
||||
|
||||
if (key && val) {
|
||||
params[key.name] = val;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function updateRoute() {
|
||||
var next = parseRoute(),
|
||||
last = $route.current;
|
||||
|
||||
if (next && last && next.$$route === last.$$route
|
||||
&& angular.equals(next.pathParams, last.pathParams)
|
||||
&& !next.reloadOnSearch && !forceReload) {
|
||||
last.params = next.params;
|
||||
angular.copy(last.params, $routeParams);
|
||||
$rootScope.$broadcast('$routeUpdate', last);
|
||||
} else if (next || last) {
|
||||
forceReload = false;
|
||||
$rootScope.$broadcast('$routeChangeStart', next, last);
|
||||
$route.current = next;
|
||||
if (next) {
|
||||
if (next.redirectTo) {
|
||||
if (angular.isString(next.redirectTo)) {
|
||||
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
|
||||
.replace();
|
||||
} else {
|
||||
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
|
||||
.replace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$q.when(next).
|
||||
then(function() {
|
||||
if (next) {
|
||||
var locals = angular.extend({}, next.resolve),
|
||||
template, templateUrl;
|
||||
|
||||
angular.forEach(locals, function(value, key) {
|
||||
locals[key] = angular.isString(value) ?
|
||||
$injector.get(value) : $injector.invoke(value);
|
||||
});
|
||||
|
||||
if (angular.isDefined(template = next.template)) {
|
||||
if (angular.isFunction(template)) {
|
||||
template = template(next.params);
|
||||
}
|
||||
} else if (angular.isDefined(templateUrl = next.templateUrl)) {
|
||||
if (angular.isFunction(templateUrl)) {
|
||||
templateUrl = templateUrl(next.params);
|
||||
}
|
||||
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
|
||||
if (angular.isDefined(templateUrl)) {
|
||||
next.loadedTemplateUrl = templateUrl;
|
||||
template = $http.get(templateUrl, {cache: $templateCache}).
|
||||
then(function(response) { return response.data; });
|
||||
}
|
||||
}
|
||||
if (angular.isDefined(template)) {
|
||||
locals['$template'] = template;
|
||||
}
|
||||
return $q.all(locals);
|
||||
}
|
||||
}).
|
||||
// after route change
|
||||
then(function(locals) {
|
||||
if (next == $route.current) {
|
||||
if (next) {
|
||||
next.locals = locals;
|
||||
angular.copy(next.params, $routeParams);
|
||||
}
|
||||
$rootScope.$broadcast('$routeChangeSuccess', next, last);
|
||||
}
|
||||
}, function(error) {
|
||||
if (next == $route.current) {
|
||||
$rootScope.$broadcast('$routeChangeError', next, last, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @returns the current active route, by matching it against the URL
|
||||
*/
|
||||
function parseRoute() {
|
||||
// Match a route
|
||||
var params, match;
|
||||
angular.forEach(routes, function(route, path) {
|
||||
if (!match && (params = switchRouteMatcher($location.path(), route))) {
|
||||
match = inherit(route, {
|
||||
params: angular.extend({}, $location.search(), params),
|
||||
pathParams: params});
|
||||
match.$$route = route;
|
||||
}
|
||||
});
|
||||
// No route matched; fallback to "otherwise" route
|
||||
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns interpolation of the redirect path with the parameters
|
||||
*/
|
||||
function interpolate(string, params) {
|
||||
var result = [];
|
||||
angular.forEach((string||'').split(':'), function(segment, i) {
|
||||
if (i === 0) {
|
||||
result.push(segment);
|
||||
} else {
|
||||
var segmentMatch = segment.match(/(\w+)(.*)/);
|
||||
var key = segmentMatch[1];
|
||||
result.push(params[key]);
|
||||
result.push(segmentMatch[2] || '');
|
||||
delete params[key];
|
||||
}
|
||||
});
|
||||
return result.join('');
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
ngRouteModule.provider('$routeParams', $RouteParamsProvider);
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngRoute.$routeParams
|
||||
* @requires $route
|
||||
*
|
||||
* @description
|
||||
* The `$routeParams` service allows you to retrieve the current set of route parameters.
|
||||
*
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*
|
||||
* The route parameters are a combination of {@link ng.$location `$location`}'s
|
||||
* {@link ng.$location#methods_search `search()`} and {@link ng.$location#methods_path `path()`}.
|
||||
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
|
||||
*
|
||||
* In case of parameter name collision, `path` params take precedence over `search` params.
|
||||
*
|
||||
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
|
||||
* (but its properties will likely change) even when a route change occurs.
|
||||
*
|
||||
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
|
||||
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
|
||||
* Instead you can use `$route.current.params` to access the new route's parameters.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* // Given:
|
||||
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
|
||||
* // Route: /Chapter/:chapterId/Section/:sectionId
|
||||
* //
|
||||
* // Then
|
||||
* $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
|
||||
* </pre>
|
||||
*/
|
||||
function $RouteParamsProvider() {
|
||||
this.$get = function() { return {}; };
|
||||
}
|
||||
|
||||
ngRouteModule.directive('ngView', ngViewFactory);
|
||||
ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngRoute.directive:ngView
|
||||
* @restrict ECA
|
||||
*
|
||||
* @description
|
||||
* # Overview
|
||||
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
|
||||
* including the rendered template of the current route into the main layout (`index.html`) file.
|
||||
* Every time the current route changes, the included view changes with it according to the
|
||||
* configuration of the `$route` service.
|
||||
*
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*
|
||||
* @animations
|
||||
* enter - animation is used to bring new content into the browser.
|
||||
* leave - animation is used to animate existing content away.
|
||||
*
|
||||
* The enter and leave animation occur concurrently.
|
||||
*
|
||||
* @scope
|
||||
* @priority 400
|
||||
* @example
|
||||
<example module="ngViewExample" deps="angular-route.js" animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainCntl as main">
|
||||
Choose:
|
||||
<a href="Book/Moby">Moby</a> |
|
||||
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
||||
<a href="Book/Gatsby">Gatsby</a> |
|
||||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
||||
<a href="Book/Scarlet">Scarlet Letter</a><br/>
|
||||
|
||||
<div class="view-animate-container">
|
||||
<div ng-view class="view-animate"></div>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<pre>$location.path() = {{main.$location.path()}}</pre>
|
||||
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
|
||||
<pre>$route.current.params = {{main.$route.current.params}}</pre>
|
||||
<pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
|
||||
<pre>$routeParams = {{main.$routeParams}}</pre>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="book.html">
|
||||
<div>
|
||||
controller: {{book.name}}<br />
|
||||
Book Id: {{book.params.bookId}}<br />
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="chapter.html">
|
||||
<div>
|
||||
controller: {{chapter.name}}<br />
|
||||
Book Id: {{chapter.params.bookId}}<br />
|
||||
Chapter Id: {{chapter.params.chapterId}}
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="animations.css">
|
||||
.view-animate-container {
|
||||
position:relative;
|
||||
height:100px!important;
|
||||
position:relative;
|
||||
background:white;
|
||||
border:1px solid black;
|
||||
height:40px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.view-animate {
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
.view-animate.ng-enter, .view-animate.ng-leave {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
|
||||
display:block;
|
||||
width:100%;
|
||||
border-left:1px solid black;
|
||||
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
.view-animate.ng-enter {
|
||||
left:100%;
|
||||
}
|
||||
.view-animate.ng-enter.ng-enter-active {
|
||||
left:0;
|
||||
}
|
||||
.view-animate.ng-leave.ng-leave-active {
|
||||
left:-100%;
|
||||
}
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
|
||||
function($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/Book/:bookId', {
|
||||
templateUrl: 'book.html',
|
||||
controller: BookCntl,
|
||||
controllerAs: 'book'
|
||||
});
|
||||
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
|
||||
templateUrl: 'chapter.html',
|
||||
controller: ChapterCntl,
|
||||
controllerAs: 'chapter'
|
||||
});
|
||||
|
||||
// configure html5 to get links working on jsfiddle
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
||||
function MainCntl($route, $routeParams, $location) {
|
||||
this.$route = $route;
|
||||
this.$location = $location;
|
||||
this.$routeParams = $routeParams;
|
||||
}
|
||||
|
||||
function BookCntl($routeParams) {
|
||||
this.name = "BookCntl";
|
||||
this.params = $routeParams;
|
||||
}
|
||||
|
||||
function ChapterCntl($routeParams) {
|
||||
this.name = "ChapterCntl";
|
||||
this.params = $routeParams;
|
||||
}
|
||||
</file>
|
||||
|
||||
<file name="scenario.js">
|
||||
it('should load and compile correct template', function() {
|
||||
element('a:contains("Moby: Ch1")').click();
|
||||
var content = element('.doc-example-live [ng-view]').text();
|
||||
expect(content).toMatch(/controller\: ChapterCntl/);
|
||||
expect(content).toMatch(/Book Id\: Moby/);
|
||||
expect(content).toMatch(/Chapter Id\: 1/);
|
||||
|
||||
element('a:contains("Scarlet")').click();
|
||||
content = element('.doc-example-live [ng-view]').text();
|
||||
expect(content).toMatch(/controller\: BookCntl/);
|
||||
expect(content).toMatch(/Book Id\: Scarlet/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.directive:ngView#$viewContentLoaded
|
||||
* @eventOf ngRoute.directive:ngView
|
||||
* @eventType emit on the current ngView scope
|
||||
* @description
|
||||
* Emitted every time the ngView content is reloaded.
|
||||
*/
|
||||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
|
||||
function ngViewFactory( $route, $anchorScroll, $animate) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
priority: 400,
|
||||
transclude: 'element',
|
||||
link: function(scope, $element, attr, ctrl, $transclude) {
|
||||
var currentScope,
|
||||
currentElement,
|
||||
autoScrollExp = attr.autoscroll,
|
||||
onloadExp = attr.onload || '';
|
||||
|
||||
scope.$on('$routeChangeSuccess', update);
|
||||
update();
|
||||
|
||||
function cleanupLastView() {
|
||||
if (currentScope) {
|
||||
currentScope.$destroy();
|
||||
currentScope = null;
|
||||
}
|
||||
if(currentElement) {
|
||||
$animate.leave(currentElement);
|
||||
currentElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
var locals = $route.current && $route.current.locals,
|
||||
template = locals && locals.$template;
|
||||
|
||||
if (angular.isDefined(template)) {
|
||||
var newScope = scope.$new();
|
||||
var current = $route.current;
|
||||
|
||||
// Note: This will also link all children of ng-view that were contained in the original
|
||||
// html. If that content contains controllers, ... they could pollute/change the scope.
|
||||
// However, using ng-view on an element with additional content does not make sense...
|
||||
// Note: We can't remove them in the cloneAttchFn of $transclude as that
|
||||
// function is called before linking the content, which would apply child
|
||||
// directives to non existing elements.
|
||||
var clone = $transclude(newScope, function(clone) {
|
||||
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
|
||||
if (angular.isDefined(autoScrollExp)
|
||||
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
||||
$anchorScroll();
|
||||
}
|
||||
});
|
||||
cleanupLastView();
|
||||
});
|
||||
|
||||
currentElement = clone;
|
||||
currentScope = current.scope = newScope;
|
||||
currentScope.$emit('$viewContentLoaded');
|
||||
currentScope.$eval(onloadExp);
|
||||
} else {
|
||||
cleanupLastView();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// This directive is called during the $transclude call of the first `ngView` directive.
|
||||
// It will replace and compile the content of the element with the loaded template.
|
||||
// We need this directive so that the element content is already filled when
|
||||
// the link function of another directive on the same element as ngView
|
||||
// is called.
|
||||
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
|
||||
function ngViewFillContentFactory($compile, $controller, $route) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
priority: -400,
|
||||
link: function(scope, $element) {
|
||||
var current = $route.current,
|
||||
locals = current.locals;
|
||||
|
||||
$element.html(locals.$template);
|
||||
|
||||
var link = $compile($element.contents());
|
||||
|
||||
if (current.controller) {
|
||||
locals.$scope = scope;
|
||||
var controller = $controller(current.controller, locals);
|
||||
if (current.controllerAs) {
|
||||
scope[current.controllerAs] = controller;
|
||||
}
|
||||
$element.data('$ngControllerController', controller);
|
||||
$element.children().data('$ngControllerController', controller);
|
||||
}
|
||||
|
||||
link(scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
})(window, window.angular);
|
20554
fatomic/web/angular.js
vendored
Normal file
20554
fatomic/web/angular.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
117
fatomic/web/app.css
Normal file
117
fatomic/web/app.css
Normal file
@ -0,0 +1,117 @@
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
|
||||
margin: 0;
|
||||
|
||||
display: -moz-box;
|
||||
-moz-box-orient: vertical;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
-moz-box-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
article {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
|
||||
text-align: center;
|
||||
background-color: lightgray;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.machine {
|
||||
display: -moz-box;
|
||||
display: flex;
|
||||
|
||||
border-radius: 6px;
|
||||
padding: 1em;
|
||||
margin: 1em 0;
|
||||
|
||||
background-color: beige;
|
||||
color: #333366;
|
||||
|
||||
position: relative;
|
||||
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.machine .status {
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
|
||||
align-self: center;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
|
||||
margin-right: 16px;
|
||||
padding-right: 72px;
|
||||
background-size: 64px 64px;
|
||||
background-position: center right;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.task-failed {
|
||||
color: red
|
||||
}
|
||||
|
||||
.machine.good {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
|
||||
.machine.bad {
|
||||
background-color: salmon;
|
||||
}
|
||||
|
||||
.machine.good .status {
|
||||
background-image: url(images/build-good.svg);
|
||||
}
|
||||
|
||||
.machine.bad .status {
|
||||
background-image: url(images/build-bad.svg);
|
||||
}
|
||||
|
||||
.app {
|
||||
padding: 1em;
|
||||
float: left;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app .icon, .app .icon img {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app .emblem {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.app.good .emblem {
|
||||
background-image: url(images/app-good.png);
|
||||
}
|
29
fatomic/web/app.js
Normal file
29
fatomic/web/app.js
Normal file
@ -0,0 +1,29 @@
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var fatomic = angular.module('fatomic', [
|
||||
'ngRoute',
|
||||
'fatomicControllers',
|
||||
]);
|
||||
|
||||
fatomic.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.
|
||||
when('/', {
|
||||
templateUrl: 'partials/home.html',
|
||||
controller: 'fatomicHomeCtrl'
|
||||
}).
|
||||
when('/installation', {
|
||||
templateUrl: 'partials/installation.html'
|
||||
}).
|
||||
when('/background', {
|
||||
templateUrl: 'partials/background.html'
|
||||
}).
|
||||
when('/implementation', {
|
||||
templateUrl: 'partials/implementation.html'
|
||||
}).
|
||||
otherwise({
|
||||
redirectTo: 'partials/unknown.html',
|
||||
});
|
||||
}]);
|
||||
|
||||
})(window);
|
35
fatomic/web/controllers.js
Normal file
35
fatomic/web/controllers.js
Normal file
@ -0,0 +1,35 @@
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var fatomicControllers = angular.module('fatomicControllers', []);
|
||||
|
||||
var ROOT = '/';
|
||||
|
||||
fatomicControllers.controller('fatomicHomeCtrl', function($scope, $http) {
|
||||
var builds = [];
|
||||
|
||||
$http.get(ROOT + 'autobuilder-status.json').success(function(status) {
|
||||
var text;
|
||||
if (status.running.length > 0)
|
||||
text = 'Running: ' + status.running.join(' ') + '; load=' + status.systemLoad[0];
|
||||
else
|
||||
text = 'Idle, awaiting packages';
|
||||
|
||||
$scope.runningState = text;
|
||||
});
|
||||
|
||||
var productsBuiltPath = ROOT + 'results/tasks/build/products-built.json';
|
||||
console.log("Retrieving products-built.json");
|
||||
$http.get(productsBuiltPath).success(function(data) {
|
||||
var trees = data['trees'];
|
||||
for (var ref in trees) {
|
||||
var treeData = trees[ref];
|
||||
var refUnix = ref.replace(/\//g, '-');
|
||||
treeData.refUnix = refUnix;
|
||||
treeData.screenshotUrl = '/results/tasks/smoketest/smoketest/work-' + refUnix + '/screenshot-final.png';
|
||||
}
|
||||
$scope.trees = trees;
|
||||
});
|
||||
});
|
||||
|
||||
})(window);
|
19
fatomic/web/index.html
Normal file
19
fatomic/web/index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!doctype html>
|
||||
<html ng-app="fatomic">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="app.css" rel="stylesheet">
|
||||
<script src="angular.js"></script>
|
||||
<script src="angular-route.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script src="controllers.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div ng-view class="content"></div>
|
||||
|
||||
<footer>
|
||||
This site is maintained in the <a href="https://github.com/cgwalters/rpm-ostree">rpm-ostree</a> git module.
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
71
fatomic/web/partials/background.html
Normal file
71
fatomic/web/partials/background.html
Normal file
@ -0,0 +1,71 @@
|
||||
<article>
|
||||
<h1>Background</h1>
|
||||
<p>Fedora today is an extremely flexible system. One can find
|
||||
Fedora builds running on everything from hobbyist ARM devices,
|
||||
to workstations, to testing servers.
|
||||
</p>
|
||||
<p>
|
||||
This flexibility derives in large part from the fact that from a
|
||||
technological point of view, Fedora is a collection of packages.
|
||||
While pre-assembled "deliverables" such as the Live CDs are
|
||||
distributed by the project, they are only a temporary state.
|
||||
</p>
|
||||
<p>
|
||||
For example, as soon as they are installed, upgrading involves
|
||||
having a package manager that dynamically reassembles the system
|
||||
from newer parts in the Fedora package collection. One cannot
|
||||
file a bug against the "default offering" as a whole - a package
|
||||
must be chosen.
|
||||
</p>
|
||||
<p>
|
||||
Nearly every aspect of the Fedora infrastructure (and
|
||||
documentation) is structured in terms of packages, from
|
||||
user-facing tools such as Bugzilla and Bodhi, to developer tools
|
||||
such as Koji. The announced security updates are based on package
|
||||
names.
|
||||
</p>
|
||||
<p>
|
||||
In contrast for example, ChromeOS is delivered and updated as an
|
||||
pre-assembled atomic unit, targeted at specific hardware.
|
||||
ChromeOS is (compared to Fedora) completely inflexible, but
|
||||
fulfills a targeted role clearly well.
|
||||
</p>
|
||||
<h3>How OSTree allows a middle ground</h3>
|
||||
<p>
|
||||
Fundamentally, packages are partial filesystem trees with metadata
|
||||
- they are traditionally assembled by a package manager on every
|
||||
client machine into a complete bootable tree. It's important to
|
||||
emphasize that it is only these <emphasis>complete</emphasis>
|
||||
trees that users run.
|
||||
</p>
|
||||
<p>
|
||||
OSTree allows an OS distributor to
|
||||
ship <emphasis>multiple</emphasis> pre-built complete bootable
|
||||
filesystem trees, and furthermore, client machines can atomically
|
||||
switch between them, or even track multiple trees independently.
|
||||
</p>
|
||||
<p>
|
||||
This allows a middle ground between the two extremes of a
|
||||
combinatorial explosion of packages, and a singular OS.
|
||||
</p>
|
||||
<h3>Initial goals</h3>
|
||||
<p>
|
||||
The first goal of this project is to be an <i>additional</i>
|
||||
deployment option built in the Fedora infrastructure; possibly
|
||||
only for Fedora rawhide. Developers and testers can use OSTree to
|
||||
atomically replicate, upgrade to newer versions of, and transition
|
||||
between the pre-assembled trees produced by this build server.
|
||||
</p>
|
||||
<p>
|
||||
Notably in this phase, no common mechanism for additional software
|
||||
installation is provided. That said, individual trees can do so;
|
||||
for example <tt>server/docker-io</tt> tree can use Docker to
|
||||
install and run server container applications independent of
|
||||
OSTree.
|
||||
</p>
|
||||
<p>
|
||||
This phase does include basic integration testing on the build
|
||||
server side, which will be a major benefit to the Fedora project
|
||||
and its downstreams.
|
||||
</p>
|
||||
</article>
|
35
fatomic/web/partials/home.html
Normal file
35
fatomic/web/partials/home.html
Normal file
@ -0,0 +1,35 @@
|
||||
<article>
|
||||
<h1>Fedora Atomic Initiative</h1>
|
||||
<p>The goal of this initiative is to accelerate development and
|
||||
increase reliability of a subset of the packages in Fedora, using
|
||||
<a href="http://live.gnome.org/Projects/OSTree">OSTree</a>, and
|
||||
more specifically the
|
||||
<a href="https://github.com/cgwalters/rpm-ostree">rpm-ostree</a>
|
||||
integration project.
|
||||
</p>
|
||||
<p>OSTree is a new basis for operating system <i>deployment</i> - it
|
||||
offers fully atomic upgrades, and reliable replication of known
|
||||
state. Using rpm-ostree, (mostly) unmodified existing packages
|
||||
can be placed into an OSTree repository.
|
||||
</p>
|
||||
<p>
|
||||
<ul>
|
||||
<li><a href="#installation">Installation</a></li>
|
||||
<li><a href="#background">Background and Goals</a></li>
|
||||
<li><a href="#implementation">Implementation</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
<h3>Current build status</h3>
|
||||
<p>The contents of these trees is defined by
|
||||
the <a href="https://github.com/cgwalters/rpm-ostree/blob/master/fedostree/products.json">products.json</a>
|
||||
file.</p>
|
||||
<div ng-repeat="(ref, treedata) in trees">
|
||||
<div>
|
||||
<div>Name: {{ ref }}</div>
|
||||
<div>Description: {{ treedata.comment }}</div>
|
||||
<div>Packages: {{ treedata.packages }}</div>
|
||||
<div>VM Image: <a href="images/auto/{{ treedata.refUnix }}">Download</a></div>
|
||||
<div><a href="{{ treedata.screenshotUrl }}"><img src="{{ treedata.screenshotUrl }}" width="160" height="120"></img></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
154
fatomic/web/partials/implementation.html
Normal file
154
fatomic/web/partials/implementation.html
Normal file
@ -0,0 +1,154 @@
|
||||
<article>
|
||||
<h1>Implementation</h1>
|
||||
<h5>/usr/lib/passwd and nss-altfiles</h5>
|
||||
<p>
|
||||
A change to
|
||||
include <a href="https://sourceware.org/bugzilla/show_bug.cgi?id=16142">/usr/lib/passwd</a>
|
||||
is required; this is implemented currently by
|
||||
the <tt>nss-altfiles</tt> package. See
|
||||
also <a href="http://fedorapeople.org/~walters/Use-usr-lib-passwd-for-system-users-if-it-exists.patch">this
|
||||
patch</a> for shadow-utils.
|
||||
</p>
|
||||
<h5>/var should be dynamically populated</h5>
|
||||
<p>
|
||||
All RPMs should stop shipping files and directories
|
||||
in <tt>/var</tt>.
|
||||
See <a href="https://people.gnome.org/~walters/ostree/doc/layout.html">this
|
||||
section</a> of the OSTree documentation.
|
||||
</p>
|
||||
<p>
|
||||
RPM should cope with its database living
|
||||
in <tt>/usr/share/rpm</tt> and being immutable.
|
||||
</p>
|
||||
<h5>SELinux support</h5>
|
||||
<p>OSTree was designed from the very beginning to work with SELinux; it just
|
||||
needs some bugfixes and integration on the rpm-ostree side.</p>
|
||||
<h5>Anaconda support</h5>
|
||||
<p>Anaconda should have an OSTree backend in addition to RPM. A basic UI
|
||||
that provides a listview of shipped trees and allows picking them would
|
||||
be quite sufficient initially.</p>
|
||||
<h5>Dracut</h5>
|
||||
<p>OSTree, when replicating content from a build server, effectively reverts
|
||||
the <a href="https://fedoraproject.org/wiki/Features/DracutHostOnly">Dracut
|
||||
host-only mode</a>. Furthermore, at the moment we hardcode
|
||||
/etc/machine-id, which is a definite bug that needs to be fixed.
|
||||
Possibly systemd should support reading the machine ID from the
|
||||
kernel commandline, as it's the only host-writable area available
|
||||
in early boot.</p>
|
||||
<h3>Development area: OSTree Layering</h3>
|
||||
<p>
|
||||
This phase would be allowing "layering" of trees. For example,
|
||||
if one installs the <tt>base/minimal</tt> tree, one could imagine
|
||||
taking the <tt>strace</tt> package, and computing a new
|
||||
filesystem tree which is the union of the two.
|
||||
</p>
|
||||
<p>
|
||||
While plain standalone ELF executables would work with no
|
||||
modification, a generalization of this kind of dynamic layering
|
||||
implies a higher level above OSTree that is aware of things
|
||||
like <tt>ldconfig</tt> and <tt>gtk-update-icon-cache</tt> and how
|
||||
to trigger them when layers are combined
|
||||
</p>
|
||||
<p>
|
||||
Conceptually, this is a step back towards combinatorics. For example,
|
||||
if libvirt is a layer that could be applied on top of the base server
|
||||
layer as well as the workstation layer, then there would need to be
|
||||
some notion of dependencies.
|
||||
</p>
|
||||
<h3>Development area: Local package assembly</h3>
|
||||
<p>
|
||||
There is absolutely no reason one could not just use the package
|
||||
manager on the client side to download and assemble packages -
|
||||
but rather than operating live on your current root, OSTree
|
||||
allows setting up the chosen tree for the next boot
|
||||
atomically.
|
||||
</p>
|
||||
<p>
|
||||
The problem is making this sort of thing efficient and scalable;
|
||||
it would require careful integration of the local OSTree
|
||||
repository and the package manager caching to operate at a speed
|
||||
comparable to traditional package management.</p>
|
||||
<p>There are two primary issues. First, OSTree mandates that the
|
||||
running tree be immutable, and requires construction of a new
|
||||
tree. This is just a deep mismatch with how package managers are
|
||||
built. Second, current package managers are totally unaware of
|
||||
the existence of the OSTree repository at /ostree/repo which holds
|
||||
binaries. A naive implementation would just redownload the packages,
|
||||
which would be quite slow.
|
||||
</p>
|
||||
</p>
|
||||
<h3>Development area: Live updates</h3>
|
||||
<p>
|
||||
If one is using OSTree in a model without a separate application
|
||||
mechanism (as is the case for rpm-ostree), it is potentially
|
||||
painful to reboot just to upgrade applications.
|
||||
</p>
|
||||
<p>
|
||||
It would be quite easy to do a bind mount of the new /usr over
|
||||
top of the old. This would avoid some of the problems dpkg/rpm
|
||||
have in creating an <emphasis>partial</emphasis> view. But even
|
||||
this model would still break many real world desktop applications
|
||||
such as Firefox. Few applications handle files from an arbitrary
|
||||
new version replacing their current ones.
|
||||
</p>
|
||||
<p>
|
||||
Starting from an <emphasis>safe</emphasis> basis, it should be
|
||||
possible to engineer userspace so that many classes of upgrades
|
||||
can be applied both live and safely, without race conditions.
|
||||
</p>
|
||||
<h3>OSTree example: Bisecting Mesa</h3>
|
||||
<p>
|
||||
OSTree allows not just dual booting - one can just as easily have
|
||||
50 or more trees locally. Suppose that you're tracking Fedora
|
||||
rawhide, and an upgrade breaks Mesa+GNOME (or sound, or something
|
||||
else). You can not only easily revert to a last known good tree,
|
||||
you can use OSTree to download intermediate builds from the build
|
||||
server and <i>bisect</i> across them. Given the ability to do
|
||||
local builds from git, automating bisection across source code is
|
||||
entirely possible as well.
|
||||
</p>
|
||||
<h3>OSTree example: Server side fast CI/CD</h3>
|
||||
<p>
|
||||
OSTree <b>mandates</b> that operating system updates work entirely
|
||||
offline. Work that has been prototyped already in
|
||||
the <a href="https://wiki.gnome.org/Projects/GnomeContinuous">gnome-continuous</a>
|
||||
codebase then takes updated content from an OSTree repository, and
|
||||
incrementally updates a cached VM disk image from the host side.
|
||||
This could allow <i>booting</i> updated systemd RPMs within
|
||||
minutes after Koji builds. This is fundamentally different from
|
||||
RPM <tt>%check</tt> as we are booting the tree as it will be
|
||||
shipped to the user - whereas RPM checks may run in e.g. a RHEL6
|
||||
kernel.
|
||||
</p>
|
||||
<h3>OSTree example: Parallel installing Red Hat Enterprise Linux and Fedora</h3>
|
||||
<p>
|
||||
Many contributors to Fedora are also Red Hat engineers working on
|
||||
Red Hat Enterprise Linux. An example way to use OSTree is to have
|
||||
EL7 installed in the physical /, and install Fedora in
|
||||
/ostree/deploy/fedora. One can choose whether or not to share
|
||||
/home.
|
||||
</p>
|
||||
<h3>OSTree example: Trying rawhide safely</h3>
|
||||
<p>
|
||||
This is an obvious use case - you can run a stable release, and
|
||||
periodically try the development release on bare metal with a
|
||||
great deal of safety, particularly if you choose not to share
|
||||
/home. In this model, the only major risk is the newer kernel
|
||||
containing filesystem corrupting bugs.
|
||||
</p>
|
||||
<h3>OSTree example: "Recovery partition"</h3>
|
||||
<p>
|
||||
You can keep your OS installed in /, manage it with a traditional
|
||||
package manager, but have a small (e.g. base/minimal) tree
|
||||
installed via OSTree to use as a "recovery partition" when
|
||||
something goes wrong. Except unlike traditional recovery
|
||||
partitions, you can easily delete it if you want space, upgrade it
|
||||
offline, etc.
|
||||
</p>
|
||||
<h3>OSTree example: Reliable safe upgrades of a server cluster</h3>
|
||||
<p>
|
||||
OSTree allows taking a "cloud" like approach to a cluster of
|
||||
traditional servers. Every upgrade is atomic and (relatively)
|
||||
efficient, and can be served by any plain HTTP server.
|
||||
</p>
|
||||
</article>
|
112
fatomic/web/partials/installation.html
Normal file
112
fatomic/web/partials/installation.html
Normal file
@ -0,0 +1,112 @@
|
||||
<article>
|
||||
<h1>Installation</h1>
|
||||
<p>It is recommended currently to only use fedostree inside a
|
||||
non-essential, disposable virtual machine (or a similar physical
|
||||
machine).
|
||||
</p>
|
||||
<h3>Installation instructions (install preconfigured VM)</h3>
|
||||
<p>If you just want to experiment with complete safety, there are automatically generated prebuilt
|
||||
VM images of various kinds <a href="http://rpm-ostree.cloud.fedoraproject.org/images/auto">here</a>.
|
||||
I recommend starting with the <tt>server/docker-io</tt> VM. Download and provision in virt-manager
|
||||
by:
|
||||
<pre>
|
||||
cd /var/lib/libvirt/images
|
||||
curl http://rpm-ostree.cloud.fedoraproject.org/images/auto/fedostree-20-x86_64-buildmaster-server-docker-io/latest-qcow2.xz | xz -d > fedostree-server-docker-io.qcow2
|
||||
</pre>
|
||||
Then using e.g. <tt>virt-manager</tt>, choose "Import existing disk image".
|
||||
</p>
|
||||
<p>Skip to <b>Inside the system</b> below.</p>
|
||||
<h3>Installation instructions (inside an existing OS)</h3>
|
||||
<p>First, you should understand what you'll be doing here. OSTree
|
||||
allows dynamically parallel installing operating systems;
|
||||
(almost) all of its data goes in the new toplevel
|
||||
directory <tt>/ostree</tt>. At the end you will have a dual
|
||||
boot.
|
||||
</p>
|
||||
<p>Install the ostree package, and make sure you have ostree 2014.1
|
||||
or newer (presently in Bodhi updates-testing).</p>
|
||||
<pre>
|
||||
yum install ostree
|
||||
</pre>
|
||||
<p>
|
||||
This bit of one time initialization will both
|
||||
create <tt>/ostree</tt> for you, as well
|
||||
as <tt>/ostree/deploy/fedostree</tt>. Only a few directories are
|
||||
created, we haven't really affected the system much yet.
|
||||
</p>
|
||||
<pre>
|
||||
ostree admin os-init fedostree
|
||||
</pre>
|
||||
<p>This step tells OSTree how to find the repository you built on
|
||||
the server. You only need to do this once.</p>
|
||||
<pre>
|
||||
ostree remote add --set=gpg-verify=false fedostree http://rpm-ostree.cloud.fedoraproject.org/repo
|
||||
</pre>
|
||||
<p>We still have only initialized configuration. This next step
|
||||
will just download (but not install) a system with just <tt>@standard</tt> plus <tt>docker-io</tt>:</p>
|
||||
<pre>
|
||||
ostree pull fedostree fedostree/20/x86_64/server/docker-io
|
||||
</pre>
|
||||
<p>This step extracts the root filesystem, and updates the bootloader
|
||||
configuration:</p>
|
||||
<pre>
|
||||
ostree admin deploy --os=fedostree --karg-proc-cmdline --karg=selinux=0 fedostree:fedostree/20/x86_64/server/docker-io
|
||||
</pre>
|
||||
<p>Now, we need to copy in the storage configuration from the "host":</p>
|
||||
<pre>
|
||||
cp /etc/fstab /ostree/deploy/fedostree/current/etc
|
||||
</pre>
|
||||
<h3 id="booting">Booting the system</h3>
|
||||
<p>Your system now contains <b>both</b> a traditional Fedora install
|
||||
and an OSTree root. There is no impact on your installed system
|
||||
except for additional disk space in the <tt>/boot/loader</tt> and <tt>/ostree</tt>
|
||||
directories.
|
||||
</p>
|
||||
<p>At the GRUB prompt, instead of choosing one of the two listed
|
||||
entries, press <tt>c</tt> to get a command line. Now, enter:</p>
|
||||
<pre>
|
||||
bls_import
|
||||
</pre>
|
||||
<p>Then press <tt>Esc</tt>. You should have an additional boot menu entry,
|
||||
named <tt>ostree:fedora:0</tt>. Nagivate to it and press <tt>Enter</tt>.</p>
|
||||
|
||||
<p>Log in to the VM as <b>root</b> - there is no password.</p>
|
||||
|
||||
<h3>Inside the system</h3>
|
||||
<p>To upgrade, run as root</p>
|
||||
<pre>
|
||||
ostree admin upgrade
|
||||
</pre>
|
||||
<p>Although <tt>yum</tt> is installed, it will operate in read-only
|
||||
mode. Do not attempt to use it at the moment. See the section
|
||||
on the homepage "Development area: Local package assembly".</p>
|
||||
|
||||
<p>But with OSTree, it's possible to atomically transition between
|
||||
different complete bootable filesystem trees. Let's now try the
|
||||
<tt>server/jbossas</tt> tree:</p>
|
||||
<pre>
|
||||
ostree pull fedostree fedostree/20/x86_64/server/jbossas
|
||||
</pre>
|
||||
<p>If you look at the <a href="https://github.com/cgwalters/rpm-ostree/blob/master/fedostree/products.json">products.json</a> script
|
||||
you can see this tree does not have <tt>docker-io</tt>, but does have the <tt>@jbossas</tt> comps group.</p>
|
||||
<p>Like above, let's now deploy it:</p>
|
||||
<pre>
|
||||
ostree admin deploy --os=fedostree fedostree:fedostree/20/x86_64/server/jbossas
|
||||
systemctl reboot
|
||||
</pre>
|
||||
<p>After you reboot, note two things. First, you'll have <i>two</i>
|
||||
OSTree boot entries. That's because our previous <tt>minimal</tt>
|
||||
tree is still there. Choose the first OSTree boot entry. When you
|
||||
boot into this tree, note that you'll have
|
||||
a <tt>/usr/bin/docker</tt> binary. We have successfully atomically
|
||||
transitioned to a new filesystem tree.
|
||||
</p>
|
||||
<p>Why the triple specification of "fedostree"? First, OSTree
|
||||
allows arbitrarily named "OS"es which have independent /var. You
|
||||
could have two deployments of the same tree, say
|
||||
"feostree-testing" and "fedostree". Second, "fedostree" is the
|
||||
name of the remote. Third, a naming convention for refs includes
|
||||
an OS name prefix at the front, here "fedostree". Some or all of
|
||||
these may be different.
|
||||
</p>
|
||||
</article>
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"comment": "This file defines the set of trees that is generated by the rpm-ostree autobuilder",
|
||||
"comment": "Default/demo trees generated by the Fedora Atomic Initiative",
|
||||
|
||||
"osname": "fedostree",
|
||||
"osname": "fatomic",
|
||||
"repo": "http://rpm-ostree.cloud.fedoraproject.org/repo",
|
||||
|
||||
"gpg_key": "843833DF",
|
||||
|
Loading…
Reference in New Issue
Block a user