fedostree: Rename to "fatomic"

This commit is contained in:
Colin Walters 2014-02-21 14:17:39 -05:00
parent f14d5ab37c
commit 87aed0fa7c
15 changed files with 22124 additions and 2 deletions

15
fatomic/fedora-20.repo Normal file
View 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

View 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
View 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"] }
}
}
}

View 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
View 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

File diff suppressed because it is too large Load Diff

117
fatomic/web/app.css Normal file
View 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
View 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);

View 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
View 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>

View 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>

View 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>

View 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>

View 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>

View File

@ -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",